Guaranteeing exactly once Box::from_raw in Rust FFI
\n
The most reliable structure pairs a dedicated Rust deallocation function with the pointer handed to C. C receives the raw pointer from Box::into_raw, uses it, and later calls the Rust function to reconstruct the Box via Box::from_raw. As long as the C side invokes that function precisely once, the Rust compiler’s lifetime contract is satisfied.
\n
Likely explanation
\n
When Rust calls Box::into_raw the pointer is handed off to C. The Rust compiler drops all ownership tracking. To reclaim the allocation C must route the pointer back through a known entry point. If C ever frees the memory through a different mechanism (e.g. free() or a custom allocator) without informing Rust or if the pointer is dropped twice undefined behavior arises. The exactly once guarantee therefore rests on a single well documented deallocation path that C must follow.
\n
Confirmed facts
\n
\n - Rust's extern C ABI does not convey ownership across the boundary the caller on each side must observe the matching allocation deallocation pair.
\n - Box::into_raw produces a *mut T that the Rust compiler no longer considers owned omitting Box::from_raw leaks the heap block.
\n - Box::from_raw is only safe when the pointer originates from a matching Box::into_raw for the same type T.
\n - Using a mismatched allocator (e.g. C malloc paired with Rust alloc) invokes undefined behavior pair new delete or malloc free across the same language boundary.
\n
\n
Steps needed for this case
\n
\n - Define a pub extern C fn deallocate(ptr: *mut T) in Rust that guards with a null check and calls Box::from_raw ptr.
\n - Expose the allocated pointer to C as the return value of an extern C function that performed Box::into_raw.
\n - Document that C must call the deallocate function exactly once after it is finished with the pointer.
\n - If C may forward the pointer to other modules or code paths consider wrapping the raw pointer in a Rust token type that consumes itself on first deallocation or use std mem forget on the Rust side while providing a corresponding C side free function that the application author guarantees to invoke once.
\n
\n
Practical verification
\n
// Rust FFI pattern\nuse std::boxed::Box;\n\n#[no_mangle]\npub extern \"C\" fn rust_allocate() -> *mut i32 {\n let boxed: Box = Box::new(42);\n Box::into_raw(boxed)\n}\n\n#[no_mangle]\npub extern \"C\" fn rust_deallocate(ptr: *mut i32) {\n if !ptr.is_null() {\n let _ = Box::from_raw(ptr);\n }\n}\n
\n
Compile the example and run under valgrind --leak-check=full or cargo miri test to confirm no leaks or double frees appear in the FFI path.
\n
If the C integration may free the pointer through a different code path than the designated rust_deallocate function please specify the exact function signature that receives the pointer the recommendation may shift to a token based guard or a ManuallyDrop wrapped wrapper.