是否有可能从C中调用一个带有Vec的Rust函数?

时间:2016-05-25 12:13:30

标签: rust ffi

假设我有以下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,我只需要声明一个extern函数取int32_t,但是可以调用{{1} }}?如果是这样,怎么样?

1 个答案:

答案 0 :(得分:5)

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

由于你无法从C构造Vec,你必须在Rust中构造它,然后返回指向C的指针.C代码将拥有指向Vec的指针,然后通过在致电do_something_else时回来。

然后问题是你无法真正修改C中的Vec,除了通过创建镜像所有Rust方法的新FFI方法。

您也可能不应该使用&Vec<i32>,因为Rust引用保证不是NULL,并且从C调用时没有任何强制执行。最好采用{ {1}},断言它是非NULL并将其转换为引用。

您可能希望通过FFI边界接受C数组。 C数组是一个指针和一个长度,所以你接受这两个并重新构建一个Rust 切片(因为你不拥有该数组):

*const Vec<i32>

The Rust FFI Omnibus的强制性链接。

如果真的需要按照你的要求行事,那么它可能会是这样的:

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

并且会像(未经测试)一样使用:

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

你想在valgrind下运行它,以确保我没有做任何愚蠢的记忆。