gpt4 book ai didi

rust - 如何在 pub extern "C"fn 中返回动态长度向量?

转载 作者:行者123 更新时间:2023-11-29 07:49:30 25 4
gpt4 key购买 nike

我想在 pub extern "C"fn 中返回一个向量。由于向量具有任意长度,我想我需要返回一个带有

的结构
  1. 指向向量的指针,以及

  2. 向量中的元素个数

我当前的代码是:

extern crate libc;
use self::libc::{size_t, int32_t, int64_t};

// struct to represent an array and its size
#[repr(C)]
pub struct array_and_size {
values: int64_t, // this is probably not how you denote a pointer, right?
size: int32_t,
}

// The vector I want to return the address of is already in a Boxed struct,
// which I have a pointer to, so I guess the vector is on the heap already.
// Dunno if this changes/simplifies anything?
#[no_mangle]
pub extern "C" fn rle_show_values(ptr: *mut Rle) -> array_and_size {
let rle = unsafe {
assert!(!ptr.is_null());
&mut *ptr
};

// this is the Vec<i32> I want to return
// the address and length of
let values = rle.values;
let length = values.len();

array_and_size {
values: Box::into_raw(Box::new(values)),
size: length as i32,
}
}

#[derive(Debug, PartialEq)]
pub struct Rle {
pub values: Vec<i32>,
}

我得到的错误是

$ cargo test
Compiling ranges v0.1.0 (file:///Users/users/havpryd/code/rust-ranges)
error[E0308]: mismatched types
--> src/rle.rs:52:17
|
52 | values: Box::into_raw(Box::new(values)),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i64, found *-ptr
|
= note: expected type `i64`
= note: found type `*mut std::vec::Vec<i32>`

error: aborting due to previous error

error: Could not compile `ranges`.

To learn more, run the command again with --verbose.
-> exit code: 101

我发布了整个内容,因为我在非常有用的 Rust FFI Omnibus 中找不到返回数组/向量的示例.

这是从 Rust 返回未知大小向量的最佳方式吗?如何修复剩余的编译错误?谢谢!

Bonus q:如果我的向量在结构中的事实改变了答案,也许你也可以展示如果向量不在 Boxed 结构中(我认为这意味着它拥有的向量在堆也是)?我想很多查找此 q 的人不会已经将他们的矢量装箱。

奖励问题 2:我只返回矢量以查看其值(在 Python 中),但我不想让调用代码更改矢量。但我想没有办法让内存只读并确保调用代码不会混淆向量? const 只是为了显示意图,对吧?

Ps:我不太了解 C 或 Rust,所以我的尝试可能完全是 WTF。

最佳答案

pub struct array_and_size {
values: int64_t, // this is probably not how you denote a pointer, right?
size: int32_t,
}

首先,你是对的。您想要的 values 类型是 *mut int32_t

一般来说,请注意 C 编码风格多种多样,C 通常不“喜欢”像这样返回特殊大小的数组结构。更常见的 C API 是

int32_t rle_values_size(RLE *rle);
int32_t *rle_values(RLE *rle);

(注意:许多内部程序实际上使用大小数组结构,但这是迄今为止面向用户的库中最常见的,因为它自动与 C 中表示数组的最基本方式兼容。

在 Rust 中,这将转化为:

extern "C" fn rle_values_size(rle: *mut RLE) -> int32_t
extern "C" fn rle_values(rle: *mut RLE) -> *mut int32_t

size 函数很简单,要返回数组,只需做

extern "C" fn rle_values(rle: *mut RLE) -> *mut int32_t {
unsafe { &mut (*rle).values[0] }
}

这给出了一个指向 Vec 底层缓冲区第一个元素的原始指针,这是所有 C 风格数组的真正含义。

如果您不想为 C 提供对您数据的引用,而是想 C 提供数据,最常见的选择是允许用户传入一个缓冲区,您将数据克隆到该缓冲区中:

extern "C" fn rle_values_buf(rle: *mut RLE, buf: *mut int32_t, len: int32_t) {
use std::{slice,ptr}
unsafe {
// Make sure we don't overrun our buffer's length
if len > (*rle).values.len() {
len = (*rle).values.len()
}
ptr::copy_nonoverlapping(&(*rle).values[0], buf, len as usize);
}
}

从 C 来看,它看起来像

void rle_values_buf(RLE *rle, int32_t *buf, int32_t len);

这(浅层)将您的数据复制到可能由 C 分配的缓冲区中,然后 C 用户负责销毁该缓冲区。它还可以防止数组的多个可变副本同时 float (假设您没有实现返回指针的版本)。

请注意,您也可以将数组“移动”到 C 中,但不是特别推荐,并且涉及使用 mem::forget 并期望 C 用户显式调用销毁函数,以及要求您和用户都遵守一些可能难以构建程序的纪律。

如果你想从 C 中接收一个数组,你基本上只需要一个 *mut i32i32 对应于缓冲区开始和长度。您可以使用 from_raw_parts 函数将其组装成一个切片,然后使用 to_vec 函数创建一个拥有的 Vector,其中包含从 Rust 端分配的值。如果您不打算拥有这些值,则可以简单地传递通过 from_raw_parts 生成的切片。

但是,必须从任何一侧初始化所有值,通常为零。否则,您会调用合法的未定义行为,这通常会导致段错误(当使用 GDB 检查时,这些错误往往会令人沮丧地消失)。

关于rust - 如何在 pub extern "C"fn 中返回动态长度向量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40156545/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com