【问题标题】:Calling Rust method from C with array parameters使用数组参数从 C 调用 Rust 方法
【发布时间】:2015-09-23 03:46:23
【问题描述】:

我正在尝试从我的 C 项目中为嵌入式设备调用 Rust 代码。设备通过 UART 打印,所以我可以看到我的调用结果是什么。

以下 C 和 Rust 代码按预期工作(我省略了许多使其编译所需的样板 Rust 代码)。

C:

uint8_t input[] = {1,2,3};
uint8_t output[] = {4,5,6};
output = func(input, output);
printf("Sum: %d", output[0]);

生锈:

#[no_mangle]
pub extern fn func(input: &[u8], dst: &mut[u8]) -> u8 {
  3
}

这会按预期打印 3。但是我一直在改变作为引用传入的数组:

C:

uint8_t input[] = {1,2,3};
uint8_t output[] = {4,5,6};
func(input, output);
printf("Sum: %d", output[0]);

生锈:

#[no_mangle]
pub extern fn func(input: &[u8], dst: &mut[u8]) {
  for i in (0..1) {
      dst[i] = input[i];
  }
}

这会编译,但会打印 4 而不是预期的 1。由于某种原因,我无法更改数组的值。有什么想法吗?

编辑:C 函数声明分别是:

extern uint8_t func(uint8_t in[64], uint8_t output[64]);
extern void func(uint8_t in[64], uint8_t output[64]);

EDIT2:更新代码: C:

uint8_t input[64];
uint8_t output[64];
for(uint8_t = 0; i < 64; i++) {
    input[i] = i;
}
func(input, output);
printf("Sum: %d", output[2]);

期望输出 2。

【问题讨论】:

    标签: c rust ffi


    【解决方案1】:

    Rust 中的 &amp;[T] 与 C 中的 T []T * 不同。您应该永远不要使用借来的指针与来自 Rust 的 C 代码。在与 C 代码交互时,您还应该永远不要使用 [T]str

    曾经

    [T]strdynamically sized types,这意味着指向它们的所有指针(任何类型)都是常规指针大小的两倍。这意味着您的 C 代码传递了两个指针,而 Rust 需要 四个。你的第二个例子没有在你的脸上爆炸,这是一个小奇迹。

    Slice Arguments example from the Rust FFI Omnibus 几乎就是您想要的。

    还有FFI chapter of the Rust Book

    编辑:那些 C 签名也是伪造的;首先,Rust 可以在任何地方接受的数组大小没有限制,所以我不确定 64 来自哪里。一个模糊可比的 Rust 类型是 [u8; 64],但即使 那个 也将 仍然 不正确,因为 C 和 Rust 传递固定大小的数组不同。 C 通过引用传递它们,Rust 通过值传递它们。

    编辑 2:假设您说的是第二个 func,Rust 翻译只是:

    // C ffi signature:
    // void copy(uint8_t src[4], uint8_t dst[4]);
    #[no_mangle]
    pub unsafe extern fn copy(src: *const [u8; 4], dst: *mut [u8; 4]) {
        if src.is_null() { return; }
        if dst.is_null() { return; }
    
        // Convert to borrowed pointers.
        let src: &[u8; 4] = &*src;
        let dst: &mut [u8; 4] = &mut *dst;
    
        for (s, d) in src.iter().zip(dst.iter_mut()) {
            *d = *s;
        }
    }
    
    #[cfg(test)]
    #[test]
    fn test_copy() {
        let a = [0, 1, 2, 3];
        let mut b = [0; 4];
        unsafe { copy(&a, &mut b); }
        assert_eq!(b, [0, 1, 2, 3]);
    }
    

    【讨论】:

    • 如果 C 通过引用传递这样的数组,那么 Rust 签名将需要使用 *const [uint8_t; 64]*mut [uint8_t; 64]
    • @ChrisMorgan:更大的问题是它们一开始就不是 64 元素数组,但是是的; uint8_t [64] 作为参数的 Rust 等效项是(至少在我测试过的平台上)*mut [u8; 64]
    • 你说得对,那些签名太可怕了。但我想避免在这里为我的平台编译 libc。另外,我总是在编译时知道数组参数的大小,所以,像 *mut [u8; 64] 很好。尝试了 func(input: *mut [u8; 64], dst: *mut [u8; 64] ),但是我不允许索引或改变参数。
    • @Jambaman:我不明白这些签名与重新编译 libc 有什么关系,或者它会完成什么。我特别关心的是,在您的 C 代码中,您没有传递长度为 64 的数组,这是等待发生的缓冲区溢出。至于不能使用*mut [u8; 64],那是因为取消引用原始指针是不安全的,必须在unsafe 块内完成。同样,根据您在问题中的解释,链接的示例似乎与您想要的最接近。
    • 我在示例中添加了代码来说明我想要做什么。我基本上只是想将输入数组(我在编译时知道其大小为 64)复制到输出(大小也为 64)。
    【解决方案2】:

    我还在 Rust nightly book 中找到了很多有用的信息,其中函数“dot_product”基本上完全符合我的要求:https://doc.rust-lang.org/nightly/book/no-stdlib.html

    【讨论】:

      猜你喜欢
      • 2013-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-04
      • 2019-11-03
      • 1970-01-01
      • 2018-12-24
      相关资源
      最近更新 更多