首页 > 解决方案 > 使用 malloced 字符串创建 Rust 字符串是否安全?

问题描述

我正在使用一个返回malloced 字符串的 C API:

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);

文件说

at 的内存ptr必须事先由标准库使用的同一分配器分配。

我找不到标准库使用的分配器。

标签: stringrustunsafe

解决方案


一般来说,从不是String从 Rust 分配的字符串创建 a 是不安全的。

Rust 0.11.0到 1.31.1 使用了 jemalloc。Rust 1.32.0更改为使用系统的默认分配器。

此外,Rust 1.28.0引入了一种机制,应用程序可以使用该机制将全局分配器替换为他们选择的一个。

需要注意的是,尽管 Rust 现在默认使用系统的默认分配器,但这并不意味着 C 库使用相同的分配器,即使它是字面上的malloc. 例如,在 Windows 上,如果您使用的是使用 Visual C++ 2008 编译的 C 库,而您的 Rust 二进制文件是使用 Visual Studio 2019 构建工具编译的,那么您的进程中将加载两个C 运行时库:C 库将使用msvcr90.dll而你的 Rust 二进制文件将使用ucrtbase.dll。每个 C 运行时库管理自己的堆,因此一个分配的内存不能被另一个释放。

一个设计良好的 C 库应该提供一个函数来为库可能分配给自己的每种类型的资源释放资源。返回指向此类分配的指针或句柄的函数应该记录应该调用哪个函数来释放资源。有关设计良好的 API 的示例,请参阅有关使用 LLVM 的 C API 的其他问题。

也许您实际上并不需要String? CStr如果可能,请考虑改用。ACStr类似于 a str,所以它只是一个内存视图,它不关心它是如何分配的,但它比str. 您可以将 a 转换CStrstrusing CStr::to_strCStr必须包含 UTF-8 字符串才能成功转换)。

如果库中确实有释放字符串的函数,您可能还想编写一个包装器结构,该结构将自动处理解除分配并将 deref to CStr. 这个结构将表示一个拥有的字符串,类似于Stringor 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)
        }
    }
}

推荐阅读