Use a C malloc'd char* in Go and Rust, then free it

Tests FFI allocator discipline. In Go, copy with C.GoString then C.free the *C.char. In Rust, read via CStr::from_ptr, copy to String, then libc::free. Red flag: letting Go GC or Rust Drop manage C memory, or using CString::from_raw on C malloc'd pointers.
WHAT THIS TESTS: Cross-language FFI memory ownership and allocator discipline. The core issue is that C heap memory is invisible to Go's garbage collector and Rust's ownership system, so you must manually pair allocation and deallocation across the boundary. The interviewer is checking that you know copying is usually required for safe native use, and that freeing must use the original allocator or a library-provided free function.
A GOOD ANSWER COVERS four things in order. First, in Go via cgo: import C and include stdlib.h, call the function to get a *C.char, immediately copy it into a Go-managed string with C.GoString, then release the C buffer with C.free(unsafe.Pointer(ptr)). Second, in Rust: call the function inside an unsafe extern block to obtain a *mut c_char, read it with std::ffi::CStr::from_ptr and copy the contents into a Rust String or str, then free the original pointer. Because the memory came from C malloc, you free it with libc::free or, more robustly, with a dedicated free function exposed by the library itself. Third, explicitly note that CString::from_raw is for Rust-allocated strings only; using it on a C malloc'd pointer is undefined behavior because it may invoke the wrong allocator. Fourth, state the production rule: the library should expose its own free function rather than forcing consumers to call malloc or free directly, since the library might be compiled with a different allocator or actually be implemented in Rust.
COMMON WRONG ANSWERS include several red flags. Letting the pointer leak because Go will GC it or Rust will drop it; neither runtime manages C heap memory. In Rust, wrapping the pointer in CString and dropping it, which corrupts the heap if the global allocators differ. In Go, forgetting to free the *C.char after converting it with C.GoString. Also, calling free on the copied Go or Rust string instead of the original C pointer.
LIKELY FOLLOW-UPS the interviewer may ask next. What if the library is actually written in Rust? You still treat it as C FFI and insist on a library-provided free function, because Rust allocations must be freed by Rust. How do you make the Rust side panic-safe? Wrap the raw pointer in a struct that implements Drop to call the C free function, ensuring cleanup even during unwinding. What if the string can contain interior null bytes? CStr reads until the first null, so you would need a pointer-plus-length API instead.
ONE CONCRETE EXAMPLE. In Go: ptr := C.get_message(); s := C.GoString(ptr); C.free(unsafe.Pointer(ptr)); then use s. In Rust: let ptr = unsafe { get_message() }; let s = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }; unsafe { libc::free(ptr as *mut libc::c_void); } then use s.
Source: https://gist.github.com/jsternberg/3adac9a7b3386d7eb53846e47955604b
Read the original → gist.github.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.