I want to call some external functions written in Rust from Go with a reference to a slice.
I have the following Rust code:
extern crate libc;
#[no_mangle]
pub extern "C" fn callme(data: &mut [libc::c_double]) -> i32 {
data.len() as i32
}
This function is made available for the cgo compiler through this C-style header file:
#IFNDEF BOGUSLIB_H
#DEFINE BOGUSLIB_H
extern int callme(double* data);
#ENDIF
I can now call this function from Go with the Rust crate compiled as a cdylib:
//#cgo CFLAGS: -Ipath/to/libfolder
//#cgo LDFLAGS: -Lpath/to/libfolder -lboguslib
//#include <boguslib.h>
import "C"
import (
"unsafe"
. "fmt"
)
func CallmeExternal() {
data := make([]float64, 1, 1)
data[0] = 1.0
ptr := (*C.double)(unsafe.Pointer(&data[0]))
size := C.callme(ptr)
printf("size %v",size)
}
The Go code uses the unsafe pointer trick to access the backing array, since a slice is defined as follows
type Slice struct {
data *byte
uint32 len
uint32 cap
}
When I execute the code above, the length of the passed reference is incredibly large. How do I access the actual data, and what is at this moment being returned?
According to The Rust FFI Omnibus as provided by @matthieu-m, I have successfully rewritten the code. The function signature must accept the types understood by the target language.
The Rust function signature changed to:
#[no_mangle]
pub extern "C" fn callme(slice: *const libc::c_double, len: libc::size_t) -> libc::c_int {
let data = slice::from_raw_parts(slice, len as usize);
data.len() as i32
}
The declaration in the header file as follows:
// skip include guards
#include <stdio.h>
extern int callme(double* slice, size_t len);
And the call from Go has now changed as well
func CallmeExternal() {
data := make([]float64, 2, 2)
data[0] = 1.0
ptr := (*C.double)(unsafe.Pointer(&data[0]))
len := C.size_t(len(data))
size := C.callme(ptr, len)
printf("size %v",size)
}
This returns 2
.