我想在a中返回一个向量pub extern "C" fn.由于向量具有任意长度,我想我需要返回一个结构
指向矢量的指针,和
向量中的元素数量
我目前的代码是:
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>,
}
Run Code Online (Sandbox Code Playgroud)
我得到的错误是
$ 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
Run Code Online (Sandbox Code Playgroud)
我发布了整个事情,因为我找不到在非常有用的Rust FFI Omnibus中返回数组/向量的示例.
这是从Rust返回未知大小的矢量的最佳方法吗?如何修复剩余的编译错误?谢谢!
Bonus q:如果我的vector在struct中的事实改变了答案,也许你也可以展示如果向量不在Boxed结构中的话怎么做(我认为它所拥有的向量也在堆上) )?我想很多人看这个q都不会把他们的矢量装箱了.
奖励q2:我只返回向量来查看其值(在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,
}
Run Code Online (Sandbox Code Playgroud)
首先,你是对的.你想要的类型values是*mut int32_t.
一般来说,并注意到有各种各样的C编码样式,C通常不会"喜欢"返回像这样的ad-hoc大小的数组结构.更常见的C API是
int32_t rle_values_size(RLE *rle);
int32_t *rle_values(RLE *rle);
Run Code Online (Sandbox Code Playgroud)
(注意:许多内部程序实际上使用大小的数组结构,但这是面向用户库最常见的,因为它自动与C中表示数组的最基本方式兼容).
在Rust中,这将转换为:
extern "C" fn rle_values_size(rle: *mut RLE) -> int32_t
extern "C" fn rle_values(rle: *mut RLE) -> *mut int32_t
Run Code Online (Sandbox Code Playgroud)
该size函数很简单,只需返回数组即可
extern "C" fn rle_values(rle: *mut RLE) -> *mut int32_t {
unsafe { &mut (*rle).values[0] }
}
Run Code Online (Sandbox Code Playgroud)
这给出了一个指向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);
}
}
Run Code Online (Sandbox Code Playgroud)
从C看起来像
void rle_values_buf(RLE *rle, int32_t *buf, int32_t len);
Run Code Online (Sandbox Code Playgroud)
这(浅)将您的数据复制到可能是C分配的缓冲区中,然后由C用户负责销毁.它还可以防止数组的多个可变副本同时浮动(假设您没有实现返回指针的版本).
请注意,您也可以将数组"移动"到C中,但不特别推荐使用并且需要使用mem::forget并期望C用户显式调用销毁函数,并要求您和用户遵守某些规则这可能很难构建程序.
如果你想从C 接收一个数组,你基本上只需要一个*mut i32和i32对应的缓冲区起始和长度.您可以使用该from_raw_parts函数将其组合成一个切片,然后使用该to_vec函数创建一个包含从Rust端分配的值的拥有Vector.如果您不打算需要拥有这些值,您可以简单地传递您生成的切片from_raw_parts.
但是,必须从任一侧初始化所有值,通常为零.否则,您调用合法的未定义行为,这通常会导致分段错误(当使用GDB检查时,这会导致令人沮丧的消失).