创建包含字符串的静态C结构

我正在尝试在Rust中创建一个动态库,它导出一个结构作为符号,将通过dlopen()加载到C程序中。

但是,当我访问结构中的第二个字符串时,我遇到了一些段错误,所以我做了一个小测试程序,试着弄清楚我做错了什么。

这是Rust代码(test.rs),使用“rustc –crate-type dylib test.rs”编译:

#[repr(C)] pub struct PluginDesc { name: &'static str, version: &'static str, description: &'static str } #[no_mangle] pub static PLUGIN_DESC: PluginDesc = PluginDesc { name: "Test Plugin\0", version: "1.0\0", description: "Test Rust Plugin\0" }; 

这是尝试加载库(test.c)的C程序,使用“gcc test.c -ldl -o test”编译:

 #include  #include  typedef struct { const char *name; const char *version; const char *description; } plugin_desc; int main(int argc, char **argv) { void *handle; plugin_desc *desc; handle = dlopen("./libtest.so", RTLD_LOCAL | RTLD_LAZY); if (!handle) { printf("failed to dlopen: %s\n", dlerror()); return 1; } desc = (plugin_desc *) dlsym(handle, "PLUGIN_DESC"); if (!desc) { printf("failed to dlsym: %s\n", dlerror()); return 1; } printf("name: %p\n", desc->name); printf("version: %p\n", desc->version); printf("description: %p\n", desc->description); return 0; } 

这是输出:

 name: 0x7fa59ef8d750 version: 0xc description: 0x7fa59ef8d75c 

如您所见,desc-> version的地址实际上是0xc(12),这是第一个字符串的长度。 因此,看起来打包到库中的结构也包含内存地址之后的字符串长度。

我在这里使用错误的字符串类型吗? 正如您所看到的,我还必须手动将字符串NULL终止。 我尝试使用CString包装器,但在这种情况下似乎不起作用(“静态项目不允许有析构函数”)。

我在Linux上运行最新的Rust night:

 $ rustc --version rustc 0.12.0-pre-nightly (f8426e2e2 2014-09-16 02:26:01 +0000) 

切片的布局( &[T]&str )是一个指针后跟一个长度,如std::raw模块的Slice结构所记录的 。 这就是为什么从C代码中读取version字段会显示name字段值的长度。 (但请注意,切片的确切内存布局不被认为是稳定的,因此在以后的版本中可能会更改。在任何情况下,都不应将特定于Rust的数据类型传递给C;只传递基本类型 – 包括raw指针 – 和#[repr(C)]注释的类型。)

编辑:不幸的是,现在似乎没有办法在Rust中这样做。 有些函数可以从切片中获取原始指针,但静态初始值设定项中不允许函数调用 。 正如评论中的sellibitze所建议的那样,您应该在C源文件中定义该变量。

简短的回答是你不能静态地分配这样的结构。 Future Rust可能会获得这种能力。

你可以做的是,静态地分配一个包含空指针的结构,并在调用函数时将这些空指针设置为有用的东西。 Rust有static mut 。 它需要不安全的代码 ,根本不是线程安全的 ,并且(据我所知)被认为是代码气味

就在这里,我认为这是一个解决方法,因为没有办法在静态中将&[T]转换为*const T

 static S: &'static [u8] = b"http://example.org/eg-amp_rust\n\0"; static mut desc: LV2Descriptor = LV2Descriptor { amp_uri: 0 as *const libc::c_char, // ptr::null() isn't const fn (yet) }; #[no_mangle] pub extern fn lv2_descriptor(index: i32) -> *const LV2Descriptor { let ptr = S.as_ptr() as *const libc::c_char; unsafe { desc.amp_uri = ptr; &desc as *const LV2Descriptor } } 

从重复的问题复制回答