如何将一个 &str 转换为 *const u8?

9

我有一个函数

extern "C" {
    fn log_impl(ptr: *const u8);
}

fn log(s: &str) {
    log_impl(s.as_bytes() as *const u8);
}

这给我带来了以下错误:
error[E0606]: casting `&[u8]` as `*const u8` is invalid
 --> src/main.rs:6:14
  |
6 |     log_impl(s.as_bytes() as *const u8);
  |              ^^^^^^^^^^^^^^^^^^^^^^^^^

与我要做的最相似的问题是将str转换为&[u8]

(将字符串转换为字节数组)


1
我建议您查看FFI模块。例如,这里有https://doc.rust-lang.org/std/ffi/struct.CString.html。 - squiguy
2个回答

19

Rust字符串与大多数C函数所期望的NUL结尾不同。您可以通过使用&s.as_bytes()[0] as *const u8s.as_ptr()&str转换为*const u8,但这对于任何期望一个以NUL结尾的字符串的C函数都无效。

相反地,您可能需要使用CString,它会将字符串复制到缓冲区并在末尾添加一个NUL终止符号。以下是假设log_impl不存储指向该字符串的指针的示例:

fn log(s: &str) {
    unsafe {
        let c_str = CString::new(s).unwrap();
        log_impl(c_str.as_ptr() as *const u8);
    }
}

4
这个函数可能不需要被标记为unsafe,取而代之它可以在内部使用一个unsafe代码块。 - Shepmaster
1
我同意。我这样做只是为了让示例更短,并且仍然可以复制粘贴到Rust playground中。我编辑它以添加不安全块。 - Jordan Miner
作为一个 Rust 新手,假设 &str 最初来自某个 String,我们能否使用 s.push("\0") 预加载原始的 String?这样会起作用吗,还是我漏掉了什么? - Jay Sullivan
@JaySullivan 是的,如果“字符串”具有0字节的容量而无需重新分配,则这样做将起作用并且比使用CString更有效率。 CString只做两件事:检查字符串中间是否有0字节,并在末尾添加0字节(这意味着它通常必须复制整个字符串)。 - Jordan Miner
为了避免不必要的堆分配,我使用了一个我编写的函数(链接在此:https://github.com/jminer/clear-coat/blob/068f247ce84017583cc49a257d84e659137e6c4f/src/attributes.rs#L17),它检查一个 &str 是否已经以 \0 结尾,如果是,则直接使用它,否则将其复制到栈上(如果足够小),最后再考虑将其复制到堆上(由 SmallVec 库处理栈和堆之间的选择)。 - Jordan Miner

1

&[u8]在Rust中被称为切片,*u8是原始指针。您可以使用from_ptr()as_ptr()在这两种类型之间来回转换。

extern "C" {
    fn log_impl(ptr: *const u8);
}

fn log(s: &str) {
    log_impl(s.as_bytes().as_ptr() as *const u8);
}

1
Rust字符串没有以NUL结尾,使用FFI进行此操作是危险的。 - Sunding Wei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接