创建包含字符串的静态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 } }
从重复的问题复制回答