如何在Rust FFI中发布常量字符串?

poo*_*lie 6 ffi rust

我想让Rust库const char *向C 公开一个静态字符串,以便与现有接口(特别是librsync)兼容.也就是说,C头文件有

extern char const *my_string;
Run Code Online (Sandbox Code Playgroud)

在C中,图书馆就是这样

char const *my_string = "hi";
Run Code Online (Sandbox Code Playgroud)

在Rust我尝过类似的东西

pub static my_string: *const libc::c_char = unsafe { "hi\0" as *const libc::c_char };
Run Code Online (Sandbox Code Playgroud)

但这抱怨

error: casting `&'static str` as `*const i8` is invalid
Run Code Online (Sandbox Code Playgroud)

似乎我不能使用CString等因为它们不是编译时常量表达式.

She*_*ter 5

这有点奇怪,所以忍受我...

#[repr(C)]
pub struct StaticCString(*const u8);
unsafe impl Sync for StaticCString {}

#[no_mangle]
pub static CONST_C_STR: StaticCString =
    StaticCString(b"a constant c string\0" as *const u8);
Run Code Online (Sandbox Code Playgroud)

理想情况下,我们可以只有一个公共的,静态的,未拼写的,指向某些字节的常量指针,对吧?但是,当您收到此错误时,您无法执行此操作:

error: the trait `core::marker::Sync` is not implemented for the type `*const u8` [E0277]
    pub static CONST_C_STR: *const u8 = b"a constant c string\0" as *const u8;
                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

我没有花很多时间思考为什么会出现这种情况.看起来像一个不可变的指针应该不会造成太大的麻烦......

但是,我们可以告诉编译器我们的类型在多线程环境中使用是安全的.这就是包装类型存在的原因 - 它允许我们实现Sync我们拥有的类型.单元素结构应始终具有与包装值相同的实现,但我们继续并将其标记为repr(C)安全.

这适用于简单的C程序:

#include <stdio.h>

extern char * CONST_C_STR;

int main(int argc, char *argv[]) {
  printf("%s\n", CONST_C_STR);
}
Run Code Online (Sandbox Code Playgroud)