首页 > 解决方案 > 如何将一片特征对象传递给C

问题描述

我最近一直在研究一个库,我想为它做 C 绑定。这个库中的一个结构体向用户返回了一个特征对象的片段。这是函数定义的样子:

pub fn controllers(&self) -> &[Box<dyn Controller>] {
    &self.controllers
}

我不确定如何翻译这个。C 代码不会在这些动态对象中插入。它只会将它们传递回 rust 代码,即

pub fn controller_get_name(controller: *const dyn Controller) -> *const c_char {
    let controller = controller.as_ref();
    controller.get_name().as_ptr();

}

目前,我有这个:

#[no_mangle]
pub extern "C" fn libvibrant_instance_get_controllers(instance: *mut Instance,
                                                      mut controllers: *const dyn Controller,
                                                      len: *mut usize) {
    assert!(!instance.is_null());
    assert!(!len.is_null());

    let instance = unsafe { instance.as_ref().unwrap() };
    controllers = instance.controllers().as_ptr();
    unsafe {
        *len = instance.controllers().len();
    }
}

但显然,这不起作用,因为as_ptr返回的是 a*const Box<dyn Controller>而不是*const dyn Controller. 我可以在这里做什么?

标签: rustffi

解决方案


在编写 C 绑定时,应该从 C 端考虑,要提供哪些操作,尽可能隐藏实现。

例如,您说 C 代码将使用 trait 对象回调 Rust;并且您似乎想将它们作为(指针,长度)对传递给 C,以便 C 可以迭代它们。然而,这意味着 C 需要知道每个特征对象有多大(比单个指针大;对于共享切片也是如此)。

相反,我会为集合和关联函数提供一个不透明的句柄来迭代它。只有当性能非常重要时,我才会让 C 知道每个元素的大小(这意味着 ABI 将取决于 Rust 如何表示 DST)。

有关技术细节,请查看诸如Rust 中的“胖指针”是什么?.


推荐阅读