是否可以从 C 调用一个带 Vec 的 Rust 函数?
Is it possible to call a Rust function taking a Vec from C?
假设我有以下 Rust 库:
// lib.rs
#![crate_type = staticlib]
#[no_mangle]
pub extern fn do_something(number: i32) {
// something
}
#[no_mangle]
pub extern fn do_something_else(collection: &Vec<i32>) {
// something
}
我知道,要从 C 调用 do_something
,我只需要声明一个采用 int32_t
的 extern
函数,但是是否可以调用 [=14] =]?如果是,怎么做?
你可以,但更好的问题是你应该吗?
因为你不能从 C 构造一个 Vec
,你必须在 Rust 中构造它,然后 return 一个指向 C 的指针。C 代码将拥有指向 [=13] 的指针=] 然后在调用 do_something_else
.
时将其传回
那么问题是你不能真正修改 C 中的 Vec
,除非创建新的 FFI 方法来镜像所有 Rust 方法。
你也可能不应该使用 &Vec<i32>
因为 Rust 引用 保证 不为 NULL,并且当从 C 调用时没有任何强制执行。它是最好采用 *const Vec<i32>
,断言它是非 NULL 并将其转换为引用。
您可能希望通过 FFI 边界接受 C 数组。 C 数组是一个指针和一个长度,所以你会接受两者并重新构造一个 Rust slice(因为你不会拥有该数组):
use std::slice;
pub extern fn do_something_else(p: *const i32, len: libc::size_t) {
let slice = unsafe {
assert!(!p.is_null());
slice::from_raw_parts(p, len)
};
}
强制性 link 至 The Rust FFI Omnibus。
如果你真的需要做你要求的事情,它可能看起来像这样:
extern crate libc;
#[no_mangle]
pub extern fn make_vec() -> *mut Vec<i32> {
Box::into_raw(Box::new(Vec::new()))
}
#[no_mangle]
pub extern fn add_number(vec: *mut Vec<i32>, val: libc::int32_t) {
let vec = unsafe {
assert!(!vec.is_null());
&mut *vec
};
vec.push(val);
}
#[no_mangle]
pub extern fn print_vec(vec: *const Vec<i32>) {
let vec = unsafe {
assert!(!vec.is_null());
&*vec
};
println!("{:?}", vec);
}
#[no_mangle]
pub extern fn drop_vec(vec: *mut Vec<i32>) {
unsafe {
assert!(!vec.is_null());
Box::from_raw(vec);
}
}
并且会像(未经测试)一样使用:
// Add extern declarations
int main(int argc, char *argv[]) {
void *v = make_vec(); // Use a real typedef here
add_number(v, 42);
print_vec(v);
drop_vec(v);
}
您需要 运行 在 valgrind 下执行此操作以确保我没有在内存方面做任何愚蠢的事情。
假设我有以下 Rust 库:
// lib.rs
#![crate_type = staticlib]
#[no_mangle]
pub extern fn do_something(number: i32) {
// something
}
#[no_mangle]
pub extern fn do_something_else(collection: &Vec<i32>) {
// something
}
我知道,要从 C 调用 do_something
,我只需要声明一个采用 int32_t
的 extern
函数,但是是否可以调用 [=14] =]?如果是,怎么做?
你可以,但更好的问题是你应该吗?
因为你不能从 C 构造一个 Vec
,你必须在 Rust 中构造它,然后 return 一个指向 C 的指针。C 代码将拥有指向 [=13] 的指针=] 然后在调用 do_something_else
.
那么问题是你不能真正修改 C 中的 Vec
,除非创建新的 FFI 方法来镜像所有 Rust 方法。
你也可能不应该使用 &Vec<i32>
因为 Rust 引用 保证 不为 NULL,并且当从 C 调用时没有任何强制执行。它是最好采用 *const Vec<i32>
,断言它是非 NULL 并将其转换为引用。
您可能希望通过 FFI 边界接受 C 数组。 C 数组是一个指针和一个长度,所以你会接受两者并重新构造一个 Rust slice(因为你不会拥有该数组):
use std::slice;
pub extern fn do_something_else(p: *const i32, len: libc::size_t) {
let slice = unsafe {
assert!(!p.is_null());
slice::from_raw_parts(p, len)
};
}
强制性 link 至 The Rust FFI Omnibus。
如果你真的需要做你要求的事情,它可能看起来像这样:
extern crate libc;
#[no_mangle]
pub extern fn make_vec() -> *mut Vec<i32> {
Box::into_raw(Box::new(Vec::new()))
}
#[no_mangle]
pub extern fn add_number(vec: *mut Vec<i32>, val: libc::int32_t) {
let vec = unsafe {
assert!(!vec.is_null());
&mut *vec
};
vec.push(val);
}
#[no_mangle]
pub extern fn print_vec(vec: *const Vec<i32>) {
let vec = unsafe {
assert!(!vec.is_null());
&*vec
};
println!("{:?}", vec);
}
#[no_mangle]
pub extern fn drop_vec(vec: *mut Vec<i32>) {
unsafe {
assert!(!vec.is_null());
Box::from_raw(vec);
}
}
并且会像(未经测试)一样使用:
// Add extern declarations
int main(int argc, char *argv[]) {
void *v = make_vec(); // Use a real typedef here
add_number(v, 42);
print_vec(v);
drop_vec(v);
}
您需要 运行 在 valgrind 下执行此操作以确保我没有在内存方面做任何愚蠢的事情。