[英]How do I return an vector of dynamic length in a pub extern “C” fn?
我想在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>,
}
我得到的錯誤是
$ 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返回未知大小的向量的最佳方法嗎? 如何解決剩余的編譯錯誤? 謝謝!
獎勵q:如果我的向量在結構中的事實改變了答案,也許您還可以顯示如果向量尚未在Boxed結構中的話,該如何做(我認為這也意味着它擁有的向量也在堆中) )? 我猜很多人在查詢這個q時都不會將其向量裝箱。
獎勵q2:我只返回向量以查看其值(在Python中),但我不想讓調用代碼更改向量。 但是我想沒有辦法將內存設為只讀,並確保調用代碼不會與向量混淆嗎? const
僅用於顯示意圖,對嗎?
附:我不太了解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用戶負責銷毀該緩沖區。 它還可以防止數組的多個可變副本同時浮動(假設您未實現返回指針的版本)。
請注意,您也可以將數組“移動”到C中,但是不建議這樣做,它涉及使用mem::forget
並期望C用戶顯式調用銷毀函數,同時要求您和用戶遵守一些可能很難圍繞程序進行構造的紀律。
如果要從C 接收數組,則實際上只需要輸入*mut i32
和i32
*mut i32
, i32
對應於緩沖區的開始和長度。 您可以使用from_raw_parts
函數將其組合成一個切片,然后使用to_vec
函數創建一個擁有的Vector,其中包含從Rust側分配的值。 如果您不打算擁有這些值,則可以簡單地通過from_raw_parts
傳遞您生成的切片。
但是,必須從任一側初始化所有值,通常初始化為零。 否則,您將調用合法的未定義行為,這通常會導致分段錯誤(在使用GDB進行檢查時,這些錯誤通常會令人沮喪地消失)。
有多種將數組傳遞給C的方法。
首先,盡管C 具有固定大小數組的概念( int a[5]
具有int[5]
類型,並且sizeof(a)
將返回5 * sizeof(int)
),但無法直接傳遞數組到函數或從中返回數組。
另一方面,可以將固定大小的數組包裝在struct
並返回該struct
。
此外,在使用數組時,必須初始化所有元素,否則, memcpy
技術上具有未定義的行為(因為它正在從未定義的值讀取),並且valgrind肯定會報告該問題。
使用動態數組
動態數組是在編譯時長度未知的數組。
如果不知道合理的上限,或者認為該界限太大而無法按值傳遞,則可以選擇返回動態數組。
有兩種方法可以處理這種情況:
它們在分配內存的人方面有所不同:前者比較簡單,但可能需要提示一種合適的大小,或者在大小不合適的情況下“倒帶”。
要求C傳遞適當大小的緩沖區
// file.h
int rust_func(int32_t* buffer, size_t buffer_length);
// file.rs
#[no_mangle]
pub extern fn rust_func(buffer: *mut libc::int32_t, buffer_length: libc::size_t) -> libc::c_int {
// your code here
}
請注意存在std::slice::from_raw_parts_mut
可以將指針+長度轉換為可變切片(在將其設為切片之前,請先將其初始化為0或要求客戶端)。
分配一個緩沖區並將其返回給C
// file.h
struct DynArray {
int32_t* array;
size_t length;
}
DynArray rust_alloc();
void rust_free(DynArray);
// file.rs
#[repr(C)]
struct DynArray {
array: *mut libc::int32_t,
length: libc::size_t,
}
#[no_mangle]
pub extern fn rust_alloc() -> DynArray {
let mut v: Vec<i32> = vec!(...);
let result = DynArray {
array: v.as_mut_ptr(),
length: v.len() as _,
};
std::mem::forget(v);
result
}
#[no_mangle]
pub extern fn rust_free(array: DynArray) {
if !array.array.is_null() {
unsafe { Box::from_raw(array.array); }
}
}
使用固定大小的數組
類似地,可以使用包含固定大小數組的struct
。 注意,在Rust和C中,即使未使用,所有元素都應初始化。 將它們清零效果很好。
與動態情況類似,它可以通過可變指針傳遞,也可以由值返回。
// file.h
struct FixedArray {
int32_t array[32];
};
// file.rs
#[repr(C)]
struct FixedArray {
array: [libc::int32_t; 32],
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.