如何从 Rust 库访问全局 C 结构数组?
How can I access a global C array of structs from a Rust library?
我正在将静态库移植到 Rust,它将与提供全局数组的 C 应用程序链接。这是结构和数组的定义:
typedef struct glkunix_argumentlist_struct {
char *name;
int argtype;
char *desc;
} glkunix_argumentlist_t;
extern glkunix_argumentlist_t glkunix_arguments[];
没有长度参数,数组中的最后一个条目将是 {NULL, 0, NULL}
(example),您在遍历数组时测试它。
我认为这是结构的正确 Rust 表示:
#[repr(C)]
struct GlkUnixArgument {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
The Rust Nomicon shows how to define an extern for a single integer.
我看到如果您有长度参数 you can use std::slice::from_raw_parts
to get a slice,但我们还没有长度。我可以修改 C 代码以提供一个,但如果可以的话,我希望能够提供一个直接替换。
我还没有看到如何从外部创建一个单一的 Rust 结构。虽然我只是想到 std::slice::from_raw_parts
的长度为 1.
有没有更好的方法可以更直接地在 Rust 中定义外部结构?
在使用 from_raw_parts
:
创建切片之前,您必须计算 C 数组的长度
use libc::{c_char, c_int};
#[repr(C)]
pub struct GlkUnixArgument {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
pub unsafe fn glkunix_arguments() -> &'static [GlkUnixArgument] {
extern "C" {
pub static glkunix_arguments: *const GlkUnixArgument;
}
let len = (0..)
.take_while(|i| {
let arg = glkunix_arguments.offset(*i);
(*arg).name != std::ptr::null()
|| (*arg).argtype != 0
|| (*arg).desc != std::ptr::null()
})
.count();
std::slice::from_raw_parts(glkunix_arguments, len)
}
您甚至可以将 C 结构映射到 Rust 结构:
use libc::{c_char, c_int, strlen};
use std::{ptr, slice, str};
pub struct GlkUnixArgument {
pub name: &'static str,
pub argtype: i32,
pub desc: &'static str,
}
pub unsafe fn glkunix_arguments() -> Vec<GlkUnixArgument> {
extern "C" {
pub static glkunix_arguments: *const GlkUnixArgument_;
}
#[repr(C)]
pub struct GlkUnixArgument_ {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
impl GlkUnixArgument_ {
fn is_not_empty(&self) -> bool {
self.name != ptr::null() || self.argtype != 0 || self.desc != ptr::null()
}
}
unsafe fn c_str_to_rust_str(s: *const i8) -> &'static str {
str::from_utf8(slice::from_raw_parts(s as *const u8, strlen(s))).unwrap()
}
let len = (0..)
.map(|i| glkunix_arguments.offset(i))
.take_while(|&arg| (*arg).is_not_empty())
.count();
slice::from_raw_parts(glkunix_arguments, len)
.iter()
.map(|args| GlkUnixArgument {
name: c_str_to_rust_str(args.name),
desc: c_str_to_rust_str(args.desc),
argtype: args.argtype,
})
.collect()
}
使用延迟构造可以确保您只执行一次操作。
我正在将静态库移植到 Rust,它将与提供全局数组的 C 应用程序链接。这是结构和数组的定义:
typedef struct glkunix_argumentlist_struct {
char *name;
int argtype;
char *desc;
} glkunix_argumentlist_t;
extern glkunix_argumentlist_t glkunix_arguments[];
没有长度参数,数组中的最后一个条目将是 {NULL, 0, NULL}
(example),您在遍历数组时测试它。
我认为这是结构的正确 Rust 表示:
#[repr(C)]
struct GlkUnixArgument {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
The Rust Nomicon shows how to define an extern for a single integer.
我看到如果您有长度参数 you can use std::slice::from_raw_parts
to get a slice,但我们还没有长度。我可以修改 C 代码以提供一个,但如果可以的话,我希望能够提供一个直接替换。
我还没有看到如何从外部创建一个单一的 Rust 结构。虽然我只是想到 std::slice::from_raw_parts
的长度为 1.
有没有更好的方法可以更直接地在 Rust 中定义外部结构?
在使用 from_raw_parts
:
use libc::{c_char, c_int};
#[repr(C)]
pub struct GlkUnixArgument {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
pub unsafe fn glkunix_arguments() -> &'static [GlkUnixArgument] {
extern "C" {
pub static glkunix_arguments: *const GlkUnixArgument;
}
let len = (0..)
.take_while(|i| {
let arg = glkunix_arguments.offset(*i);
(*arg).name != std::ptr::null()
|| (*arg).argtype != 0
|| (*arg).desc != std::ptr::null()
})
.count();
std::slice::from_raw_parts(glkunix_arguments, len)
}
您甚至可以将 C 结构映射到 Rust 结构:
use libc::{c_char, c_int, strlen};
use std::{ptr, slice, str};
pub struct GlkUnixArgument {
pub name: &'static str,
pub argtype: i32,
pub desc: &'static str,
}
pub unsafe fn glkunix_arguments() -> Vec<GlkUnixArgument> {
extern "C" {
pub static glkunix_arguments: *const GlkUnixArgument_;
}
#[repr(C)]
pub struct GlkUnixArgument_ {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
impl GlkUnixArgument_ {
fn is_not_empty(&self) -> bool {
self.name != ptr::null() || self.argtype != 0 || self.desc != ptr::null()
}
}
unsafe fn c_str_to_rust_str(s: *const i8) -> &'static str {
str::from_utf8(slice::from_raw_parts(s as *const u8, strlen(s))).unwrap()
}
let len = (0..)
.map(|i| glkunix_arguments.offset(i))
.take_while(|&arg| (*arg).is_not_empty())
.count();
slice::from_raw_parts(glkunix_arguments, len)
.iter()
.map(|args| GlkUnixArgument {
name: c_str_to_rust_str(args.name),
desc: c_str_to_rust_str(args.desc),
argtype: args.argtype,
})
.collect()
}
使用延迟构造可以确保您只执行一次操作。