当Rust FFI函数将没有#[repr(C)]的结构返回给C时,返回什么?

时间:2019-01-22 02:38:38

标签: rust ffi

编辑:有人指出,我的示例不够完整,无法使用。我的问题已经解决,但是如果您有兴趣查看完整的代码,可以here看到它。

给出以下代码:

#[no_mangle]
pub extern "C" fn create_acceptor() -> Acceptor {
    Acceptor {}
}

pub struct Acceptor {}

如果我从C调用它,它实际上会得到什么?是指针吗?某种ID?

我目前在C端使用如下代码:

extern int create_acceptor();

int main(int argc, char **argv) {
        int acceptor = create_acceptor();
        return 0;
}

似乎工作正常。我像不透明类型一样使用它,像acceptor_doSomething(acceptor)这样传递回Rust。

但是我很困惑,因为在C端对acceptor使用哪种类型似乎无关紧要。 void*int的行为相同。另外,文档似乎表明我应该使用#[repr(C)],但似乎没有它就可以工作。为什么会这样?

在打印C端接收到的值时,它显示的是非常小的整数,所以我猜它是Rust端的id?

1 个答案:

答案 0 :(得分:3)

TL; DR:该示例不是有用的示例,并且不显示任何内容。

  

实际上得到了什么?是指针吗?某种ID?

这是结构,完全符合Rust方面的定义。如果您返回一个指针(此示例未返回),则为一个指针;如果您返回某个ID(此示例未返回),则为一个id。

Rust的FFI不会增加开销或抽象-您返回的是返回的内容。

  

文档似乎表明我应该使用#[repr(C)]

是的,您应该。没有它,您的代码很可能是未定义的行为。尚未保证Rust结构的布局。如果不将表示形式指定为C,则C代码无法知道哪些字段位于何处。

  

但似乎没有它就可以工作

那是因为您的示例结构没有字段,因此没有大小(如果没有非标准扩展名,则AFAIK在C语言中甚至无效)。 Rust基本上甚至不需要查看数据(什么数据?),所以回传什么都没有关系。

  

打印在C端接收到的值时,它显示出非常小的整数

这可能只是周围堆积的垃圾。字面上未初始化的数据。

使用具有不带#[repr(C)]的字段的结构

这是不好。不要这样String是具有非平凡字段的结构,并且未标记为#[repr(C)]

Cargo.toml

[package]
name = "shear"
version = "0.1.0"
authors = ["An Devloper"]

[lib]
crate-type = ["cdylib"]

[dependencies]

src / lib.rs

// Don't do this, `String` isn't #[repr(C)]!
#[no_mangle]
pub extern "C" fn create_example() -> String {
    String::from("hello")
}

// Don't do this, `String` isn't #[repr(C)]!
#[no_mangle]
pub extern "C" fn use_example(e: String) {
    println!("{}", e);
}

main.c

extern int create_example();
extern void use_example(int example);

int main(int argc, char **argv) {
  int example = create_example();
  use_example(example);
}

执行

$ cargo build
   Compiling shear v0.1.0 (file:///home/ubuntu/shear)
    Finished dev [unoptimized + debuginfo] target(s) in 1.02s
$ gcc -o example main.c -L target/debug/ -lshear
$ LD_LIBRARY_PATH=target/debug/ ./example
Segmentation fault

请阅读The Rust FFI Omnibus,以获取有关如何在FFI边界上正确传输值的详细信息。免责声明:我是主要作者。