使用 malloced 字符串创建 Rust 字符串是否安全?
Is is safe to create a Rust String using a malloced string?
我正在使用 API returns 一个 malloc
ed 字符串:
char *foo(int arg);
我可以在不进行 O(n)
复制的情况下在 Rust 代码中重用该内存吗?
let p: *mut libc::c_char = foo(42);
let len = strlen(p);
let s: String = String.from_raw_parts(p, len, len);
文档说
The memory at ptr
needs to have been previously allocated by the same allocator the standard library uses.
我找不到标准库使用的分配器。
一般来说,从不是从 Rust 分配的字符串创建 String
是 不 安全的。
Rust 0.11.0 through 1.31.1 used jemalloc. Rust 1.32.0 更改为使用系统的默认分配器。
此外,Rust 1.28.0 引入了一种机制,应用程序可以使用该机制将全局分配器替换为他们选择的一个。
重要的是要注意,虽然 Rust 现在默认使用系统的默认分配器,但这并不意味着 C 库使用相同的分配器,即使它是字面上的 malloc
。例如,在 Windows 上,如果您使用的 C 库是用 Visual C++ 2008 编译的,而您的 Rust 二进制文件是用 Visual Studio 2019 Build Tools 编译的,那么会有 两个 在您的进程中加载的 C 运行时库:C 库将使用 msvcr90.dll 而您的 Rust 二进制文件将使用 ucrtbase.dll。每个 C 运行时库都管理自己的堆,因此一个分配的内存不能被另一个释放。
一个设计良好的 C 库应该提供一个函数来为库可能分配给自己的每种类型的资源释放资源。 return 指向或处理此类分配的函数应该记录应该调用哪个函数来释放资源。
也许您实际上不需要String
?考虑使用 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
(CStr
必须包含 UTF-8 字符串才能成功转换)。
如果库中确实有释放字符串的函数,您可能还想编写一个包装器结构来自动处理释放并将取消引用 CStr
。这个结构将代表一个拥有的字符串,类似于 String
或 CString
,但内存由库而不是 Rust 的全局分配器管理。例如:
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)
}
}
}
我正在使用 API returns 一个 malloc
ed 字符串:
char *foo(int arg);
我可以在不进行 O(n)
复制的情况下在 Rust 代码中重用该内存吗?
let p: *mut libc::c_char = foo(42);
let len = strlen(p);
let s: String = String.from_raw_parts(p, len, len);
文档说
The memory at
ptr
needs to have been previously allocated by the same allocator the standard library uses.
我找不到标准库使用的分配器。
一般来说,从不是从 Rust 分配的字符串创建 String
是 不 安全的。
Rust 0.11.0 through 1.31.1 used jemalloc. Rust 1.32.0 更改为使用系统的默认分配器。
此外,Rust 1.28.0 引入了一种机制,应用程序可以使用该机制将全局分配器替换为他们选择的一个。
重要的是要注意,虽然 Rust 现在默认使用系统的默认分配器,但这并不意味着 C 库使用相同的分配器,即使它是字面上的 malloc
。例如,在 Windows 上,如果您使用的 C 库是用 Visual C++ 2008 编译的,而您的 Rust 二进制文件是用 Visual Studio 2019 Build Tools 编译的,那么会有 两个 在您的进程中加载的 C 运行时库:C 库将使用 msvcr90.dll 而您的 Rust 二进制文件将使用 ucrtbase.dll。每个 C 运行时库都管理自己的堆,因此一个分配的内存不能被另一个释放。
一个设计良好的 C 库应该提供一个函数来为库可能分配给自己的每种类型的资源释放资源。 return 指向或处理此类分配的函数应该记录应该调用哪个函数来释放资源。
也许您实际上不需要String
?考虑使用 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
(CStr
必须包含 UTF-8 字符串才能成功转换)。
如果库中确实有释放字符串的函数,您可能还想编写一个包装器结构来自动处理释放并将取消引用 CStr
。这个结构将代表一个拥有的字符串,类似于 String
或 CString
,但内存由库而不是 Rust 的全局分配器管理。例如:
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)
}
}
}