Using arrays of iovecs in Cgo

I've been having a lot of fun with Go recently, and my usual first project when playing with a new language is to port remctl bindings to it.

In doing that with Go, I ran across a problem that had me puzzled for a while until I finally figured it out. In the remctl C binding there's a call remctl_commandv(struct remctl *r, const struct iovec *iov, size_t count); e.g. you pass remctl_commandv an array of iovec pointers and a count for how long that array is.

After quite a bit of confusion, I came around to doing this:

  • Cgo allows you to reference C types directly, so I could create _Ctype_struct_iovec items
  • Go has native arrays, but they're rarely used because they are rather inflexible. Typically you use slices instead. A slice is simply a pointer to some element of a fixed array (a slice can point to any element), and some housekeeping indicating the length of the slice and its capacity.
  • With this, you can use the make function to create a slice: iov := make([]_Ctype_struct_iovec, len(cmd)) makes a slice of C iovecs long enough to hold all the things I need to pass.
  •     
        for n, v := range cmd {
            cmd_c := C.CString(v)
            defer C.free(unsafe.Pointer(cmd_c))
            iov[n].iov_base = unsafe.Pointer(cmd_c)
            iov[n].iov_len = C.size_t(len(v))
        }       
                
        if sent := C.remctl_commandv( r.ctx, &iov[0], C.size_t(len(cmd))); sent != 1 {
            // It failed, return an error
            return r.get_error()
        }
        
        

    We loop through cmd, doing the gymnastics we need to do to set each individual iov member. Then you pass the address of the 0th element of iov — with this trick, you're not referencing iov (which, remember, is a bit of housekeeping data that eventually points to the first bit of the array of _Ctype_struct_iovecs), but the actual underlying array.

    I'll admit, this interface between Go and C makes me slightly queasy, but it works just fine. You can see the final project at the go-remctl GitHub page.