Getting the Nim -> C wrapping right can be a bit of a challenge, but once you know what's going on under the hood it's easy to see what you did wrong. So let's dive into what goes on here.
First of you started with type ClipboardObj* {.importc: "clipboard_c", header:"libclipboard/libclipboard.h".} = pointer Run This tells Nim that whenever it sees the use of `ClipboardObj` it should replace it with `clipboard_c` and otherwise treat it as a pointer in Nim code. This means that code like this: var clipboard = clipboard_new(nil) Run roughly translates to: clipboard_c clipboard; clipboard = clipboard_new(NULL); Run This is obviously not correct as `clipboard_new` returns a `clipboard_c*` and not `clipboard_c`. The initial definition of the `clipboard` variable is what causes the compiler to throw that incomplete struct error. It simply doesn't know how big `clipboard_c` is, and therefore can't allocate room for it. If we simply change the to be: type ClipboardObj* {.importc: "clipboard_c*", header:"libclipboard/libclipboard.h".} = pointer Run The problem is now fixed. Now you tell Nim that `ClipboardObj` should be treated as a pointer, and when converting it to C it should be converted as `clipboard_c*`. So why does your code work? In fact the `{.pure.}` pragma you attach to it has nothing to do with it, in doesn't do anything for object types and is only used for enums. The definitions: type ClipboardStruct* {.importc:"clipboard_c", header: "libclipboard/libclipboard.h".} = object ClipboardObj* = ptr ClipboardStruct Run by themselves would solve the problem on their own. This is because `ClipboardStruct` is now `clipboard_c` and `ClipboardObj` being defined as a pointer to that struct is now `clipboard_c*` which is the correct type. While debugging this wrapping I simply looked at what Futhark generated for these bindings. With: import futhark importc: absPath "/usr/lib/clang/13.0.0/include" absPath "/usr/include" "libclipboard.h" var clipboard = clipboard_new(nil) echo clipboard == nil echo clipboard.clipboardSetText("Hello world".cstring) clipboard.clipboardFree() Run I was able to look into the generated Nim file in the cache and peek at all the definitions. If you don't want to use Futhark yourself for wrapping I can still recommend it as a learning tool. If you're able to wrap things with Futhark (and you should be able to wrap most things entirely automatically) you should be able to just have a look at the definitions there and copy them into your own code.