== Some notes on my experience using Go's cgo system [[Cgo https://github.com/golang/go/wiki/cgo]] is the Go compiler suite's bridge to C. I recently used it to write [[a Go package that gives you access to Solaris kstat kernel statistics https://github.com/siebenmann/go-kstat/]], so I want to write down some notes on the whole thing before they fall out of my memory. On the whole, using cgo was a pleasant enough experience. I don't have very much experience in [[FFIs https://en.wikipedia.org/wiki/Foreign_function_interface]] so I can't say how cgo compares to others, but cgo makes C and Go seem like a reasonably natural fit. It often really does seem like you're just using another Go package. (With that said, there's a lot more use of _unsafe.Pointer()_ than you'll find almost anywhere else.) At the mechanical level, the most annoying thing to deal with was C unions. Go has no equivalent and cgo basically leaves you on your own to read or set union fields. I wound up just writing some trivial C functions to extract union fields for me and then had my Go code call them, rather than wrestle with casts and _unsafe.Pointer()_ and so on in Go code; the C functions were both short and less error prone for me to write. A C function was also my solution to needing to do pointer arithmetic. In C, a common approach is to define a field as '_struct whatever *ptr;_' and then say it actually points to an array of those _struct_s, with the length of the array given by some other field. You access the elements of the array by doing things like incrementing _ptr_ or indexing off it. Well, in Go that doesn't work; if you want to increment _ptr_ to the next _struct_, you're going to have to throw in explicit _C.sizeof_ invocations and so on. I decided it was simpler to do it in C instead: .pn prewrap on > kstat_named_t *get_nth_named(kstat_t *ks, uint_t n) { > kstat_named_t *knp; > if (!ks || !ks->ks_data || n >= ks->ks_ndata) > return NULL; > knp = (kstat_named_t *)ks->ks_data; > return knp + n; > } Typecasts are another one of the irritations of cgo. Cgo makes every C type into a Go type, and boy does a lot of C turn out to have a lot of different integer types. In C they mostly convert into each other without explicit casts; in Go they are all fully separate types and you must explicitly cast them around in order to interact with each other and with native Go integer types. This can get especially annoying in things like _for_ loop indexing, because if you write '_for i := 0; i < CFIELD; i ++_' the compiler will object that _i_ is a different type than _CFIELD_. This resulted in a _for_ loop that looks like this: > for i := C.uint_t(0); i < k.ksp.ks_ndata; i++ { > .... > } (I wrote more about the mechanics in [[getting C-compatible _struct_s in Go GoCGoCompatibleStructs]], [[copying memory into Go _struct_s GoMemoryToStructures]], and [[cgo's string functions explained GoCGoStringFunctions]].) At the design level, my biggest problem was handling C memory lifetime issues correctly. Part of this was figuring out where [[the C library ../solaris/KStatProgrammingNotes]] had to be using dynamic allocation (and when it got freed), and part of it was working out what it was safe for Go structures to hold references to and when those references might become invalid because of some call I made to the C library API. Working this out is vital because of [[the impact of coupling Go and C memory lifetimes together GoMemoryToStructures]], plus these memory lifetime issues are likely to have an effect on your package API. What operations can callers do or not do after others? What precautions do you need to take inside your package to try to avoid dereferencing now-free C memory if callers get the lifetime rules wrong? What things can you not expose because there's no way to guard against 'use after free' errors? And so on. ([[_runtime.SetFinalizer()_ https://golang.org/pkg/runtime/#SetFinalizer]] can help with this by letting you clean up C memory when Go memory is going away, but it's not a complete cure.) Not all uses of cgo will run into memory lifetime problems. Many are probably self-contained, where all of your interaction with C code is inside one function and when it returns you're done and can free up everything.