【问题标题】:What is returned when a Rust FFI function returns a struct without #[repr(C)] to C?当 Rust FFI 函数将没有 #[repr(C)] 的结构返回给 C 时,会返回什么?
【发布时间】:2019-06-15 11:11:20
【问题描述】:

编辑:有人指出我的示例不够完整,无法使用。我的问题已解决,但如果您有兴趣查看完整代码,可以查看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?

【问题讨论】:

  • 空类型在 C 中不是一个东西,也返回一个空类型对我来说没有多大意义,但是 void 在大多数情况下应该“工作”。但是int没有任何意义,锈空类型都是空的。
  • 是的,抱歉这不是一个很好的例子。我编辑了指向我的实际代码的链接。反正我的问题已经回答了。

标签: rust ffi


【解决方案1】:

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

ma​​in.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 边界正确传输值。免责声明:我是主要作者。

【讨论】:

  • 是的,我简化了示例以减少噪音。显然我剪得太多了。在我的实际代码中,我存储了重要的数据(TcpListener 和 TcpStream),在将它们传递给 C 并返回之后,我能够成功地在套接字上发送和接收数据。
  • @anderspitman 您在代码的 C 部分放置了一个包含 int 的结构?他们绝对不能适应。我敢打赌,如果你在 valgrind 中运行,你会得到一大堆错误。
  • 哈哈是的。我很惊讶它完全起作用了。当我有更好的整体理解时,我会阅读综合报告并报告。
  • 综合是一个很好的资源,它使我做错了什么很明显。无论如何,您的回答清楚地表明我在做什么是错误的并且可能未定义,这是正确的答案。感谢您的帮助!
  • 只是想再次报告并表示感谢。使用 Omnibus 作为指导,我能够让一切正常工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-29
  • 2019-12-28
  • 2015-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多