是否可以调用 Rust 函数并从 C 中获取 Vec?

2024-02-24

假设我有以下 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 
}

我知道,打电话do_something从 C 开始,我只需要声明一个extern函数取一个int32_t,但是可以打电话吗do_something_else?如果是这样,怎么办?


You can,但更好的问题是should you?

因为你无法构造一个Vec从 C 开始,你必须用 Rust 构造它,然后返回一个指向 C 的指针。C 代码将拥有指向Vec然后在调用时将其传回do_something_else.

那么问题来了,你无法真正修改Vec在 C 中,除了创建反映所有 Rust 方法的新 FFI 方法之外。

你可能也不应该采取&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 综合集 http://jakegoulding.com/rust-ffi-omnibus/.


If you really需要做你要求的事情,它可能看起来像这样:

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 下运行它,以确保我没有做任何愚蠢的内存操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否可以调用 Rust 函数并从 C 中获取 Vec? 的相关文章

随机推荐