能否从C语言调用接受Vec参数的Rust函数?

4
假设我有以下 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函数,但是是否可以调用do_something_else呢?如果可以,怎么做呢?

1
我认为你可以从C语言将整数数组指针传递给Rust函数。然后,你可以使用向量切片进行操作。 - noshusan
@noshusan 你的意思是像这样做吗 pub extern fn do_thing(slice: &[i32]) 然后在 C 端声明为 void do_thing(int32_t slice[]) - Romário
我不确定,但你可以像这样做:pub extern fn do_thing(slice: *[i32]),然后在C端声明它:void do_thing(int32_t* slice[])。在这里,你使用了原始指针,因此必须声明一个不安全块。 - noshusan
1个回答

9

可以这样做,但更好的问题是你应该这样做吗?

由于你无法从C中构造一个Vec,所以你需要在Rust中构造它,然后返回一个指向C的指针。C代码将拥有Vec的指针,并在调用do_something_else时将其传回。

还有一个问题是,你不能真正修改C中的Vec,除非创建新的FFI方法来镜像所有的Rust方法。

你也不应该使用&Vec<i32>,因为Rust引用被保证不为NULL,而从C调用时没有任何强制要求。最好使用*const Vec<i32>,断言它不为NULL并将其转换为引用。

很可能你想通过FFI边界接受一个C数组。C数组是一个指针和一个长度,所以你需要同时接受它们并重新构建一个Rustslice(因为你不拥有该数组):

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)
    };
}

必须提供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下运行此程序,以确保我没有在内存方面做出任何愚蠢的操作。


我的意图是将一些代码从C++项目移植到Rust中--在Rust中进行一些计算,然后获取结果并在C++端使用它们。这个想法是逐步将C++项目转移到Rust中。有什么更好的方法吗? - Romário
@Romário 这是一个没有更多信息很难回答的问题,而且可能不适合在SO上讨论。如果你有C代码,我可能会建议进行这种改变,因为这样你就可以删除手动扩展向量实现。然而,C++标准库中已经有了集合,所以你只是在两个标准库之间切换。也许你可以尝试更高一级的方法,将包含std::vector对象移植到Rust? - Shepmaster
也许你可以尝试提高一个级别,并将包含std::vector的对象移动到Rust中?FFI Omnibus上有关于此的任何指针吗? - Romário
1
@Romário 可能使用 Rust 对象与其他语言交互是最接近的匹配。 - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接