I am working with a C API that returns a malloc
ed string:
char *foo(int arg);
Can I reuse that memory in Rust code without O(n)
copying?
let p: *mut libc::c_char = foo(42);
let len = strlen(p);
let s: String = String.from_raw_parts(p, len, len);
The documentation says
The memory at
ptr
needs to have been previously allocated by the same allocator the standard library uses.
I failed to find what allocator is used by standard library.
In general, it's not safe to create a String
from a string that was not allocated from Rust.
Rust 0.11.0 through 1.31.1 used jemalloc. Rust 1.32.0 changed to use the system's default allocator.
Additionally, Rust 1.28.0 introduced a mechanism that applications can use to replace the global allocator with one of their choosing.
It's important to note that, although Rust now uses the system's default allocator by default, that doesn't mean that C libraries use the same allocator, even if it's literally malloc
. For example, on Windows, if you use a C library that's been compiled with Visual C++ 2008 while your Rust binary has been compiled with Visual Studio 2019 Build Tools, there will be two C runtime libraries loaded in your process: the C library will use msvcr90.dll while your Rust binary will use ucrtbase.dll. Each C runtime library manages its own heap, so memory allocated by one cannot be freed by the other.
A well-designed C library ought to provide a function to free resources for each type of resource that the library may allocate itself. Functions that return pointers or handles to such allocations ought to document which function should be called to free the resource(s). See this other question regarding usage of LLVM's C API for an example of a well-designed API.
Perhaps you don't actually need a String
? Consider using CStr
instead, if that's possible. A CStr
is akin to a str
, so it's just a view into memory and it doesn't care how it was allocated, but it's more permissive than str
. You can convert a CStr
to a str
using CStr::to_str
(the CStr
must contain a UTF-8 string for the conversion to succeed).
If there is indeed a function in the library to free the string, you might also want to write a wrapper struct that will handle deallocation automatically and will deref to CStr
. This struct would represent an owned string, akin to String
or CString
, but with memory managed by the library instead of Rust's global allocator. For example:
extern crate libc; // 0.2.62
use std::ffi::CStr;
use std::ops::Deref;
extern {
fn libfoo_free(string: *mut libc::c_char);
}
struct LibfooString(*mut libc::c_char);
impl Drop for LibfooString {
fn drop(&mut self) {
unsafe {
libfoo_free(self.0);
}
}
}
impl Deref for LibfooString {
type Target = CStr;
fn deref(&self) -> &Self::Target {
unsafe {
CStr::from_ptr(self.0)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With