Skip to content
tezvyn:
Menu

Pass a string from Go and Rust to C safely

Source: doc.rust-lang.orgEasyHow cards are made

This tests FFI ownership and null-termination. In Go, use C.CString then C.free it. In Rust, create a std::ffi::CString, bind it to a let, then pass as_ptr while the binding lives. Red flag: claiming Rust is auto-safe without mentioning the temp-drop gotcha.

What's really being asked

This question probes your understanding of FFI string semantics, specifically the gap between managed language strings and C's null-terminated char pointer convention. The interviewer cares whether you recognize that crossing the language boundary transfers memory ownership responsibilities and that both Go and Rust require explicit steps to produce a valid C string. You must demonstrate knowledge of allocation, null termination, pointer lifetime, and cleanup.

The full answer

A strong response hits four points in order. First, for Go, explain that you import C and use C.CString to allocate a null-terminated copy on the C heap, pass the resulting pointer to print_message, and then release it with C.free because the Go garbage collector does not manage C memory. Second, for Rust, explain that std::ffi::CString::new creates an owned, null-terminated buffer after verifying there are no interior null bytes, and you obtain a raw pointer with as_ptr. Third, emphasize the Rust lifetime gotcha: you must bind the CString to a named let binding before calling as_ptr, because invoking as_ptr on a temporary CString drops the allocation immediately and leaves a dangling pointer. Fourth, note that both languages assume UTF-8 compatibility here, and if the C function stores the pointer beyond the call duration you need a different ownership strategy.

The mistakes people make

Watch for three dangerous patterns. One is passing a Go string or byte slice directly to C, which fails because Go strings are not null-terminated and the GC may move the underlying array. Two is forgetting to call C.free in Go, which leaks memory on the C heap. Three is the Rust temporary drop bug, where a candidate writes CString::new("msg").unwrap().as_ptr() inside an unsafe block and claims Rust memory safety prevented any issues, not realizing that raw pointers bypass the borrow checker and the CString was freed before the C call used it.

What usually comes next

An interviewer might push on what happens if print_message stores the pointer for later use, which would require the string to live at least as long as the C library needs it. They may ask how you would handle a string that contains interior null bytes, forcing a discussion of length-delimited buffers or escape strategies. They could also ask about the performance overhead of allocating and copying a new null-terminated buffer on every call, or how to safely receive a string back from C into Rust or Go.

A concrete example

In Rust, the incorrect approach is let ptr = CString::new("hello").unwrap().as_ptr(); unsafe { print_message(ptr); } because the CString is dropped at the semicolon and ptr dangles. The correct approach is let c_msg = CString::new("hello").unwrap(); unsafe { print_message(c_msg.as_ptr()); } ensuring c_msg lives through the call. In Go, the incorrect approach is passing a native string directly. The correct approach is msg := C.CString("hello"); defer C.free(unsafe.Pointer(msg)); C.print_message(msg).

Interview question

In Rust, what is the danger of calling CString::new("msg").unwrap().as_ptr() directly in an unsafe block?

  • a.The resulting pointer is not null-terminated, corrupting the C function
  • b.The pointer refers to stack memory that is freed when the function returns
  • c.The temporary CString is dropped at the semicolon, leaving a dangling pointerCorrect
  • d.The borrow checker rejects the code because raw pointers cannot cross FFI boundaries
Why?

The correct answer is B because the temporary CString is dropped at the end of the statement, so the raw pointer dangles before the C call uses it. Distractor C is tempting because Rust's borrow checker normally prevents use-after-free, but raw pointers bypass those checks entirely.

Just read this? Test yourself on what you have been reading.

Read the original → doc.rust-lang.org

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.

See open roles