C++與Rust操作裸指標的比較
假設存在const T* ptr1
和T* ptr2
,分別判斷它們是否為空,C++和Rust實現如下所示。
if ((NULL == ptr1) || (nullptr == ptr2)) { // do something } 複製程式碼
use std::ptr; if ptr::null() == ptr1 || ptr::null_mut() == ptr2 { // do something } 複製程式碼
返回nullptr/NULL
由前面可知,Rust提供C介面時返回nullptr或NULL的實現如下
#[no_mangle] pub extern "C" fn init_engine() -> * const c_void { // something goes wrong std::ptr::null() } fn main() { unsafe{ let engine = init_engine(); println!("{:?}", engine); } } 複製程式碼
使用slice直接讀寫指標內容
寫指標
const int COUNT = 100; int *int_ptr = new int[COUNT]; for (int i = 0; i < COUNT; ++i) { int_ptr[i] = i; } 複製程式碼
將上述C++申請的ptr指標傳遞到Rust進行寫入,最差的辦法是在Rust內部建立一個長度相同的Vector
,將資料寫入Vector
,再通過std::ptr::copy
到int_ptr
中,示例如下:
use std::ptr; #[no_mangle] pub extern "C" fn write_to_c_buffers(n: usize, buffers: *mut i32) { let mut tmp_buffers = Vec::with_capacity(n); for index in 0..n { tmp_buffers.push(index); } unsafe { ptr::copy(tmp_buffers.as_ptr(), buffers, n); } } 複製程式碼
上述的tmp_buffers
分配了一塊與buffers
等長的新記憶體,這樣多佔用了記憶體,不科學。
使用std::slice
直接讀寫裸指標可實現前面C++式的做法,示例如下:
use std::slice; #[no_mangle] pub extern "C" fn write_to_c_buffers(n: usize, buffers: *mut i32) { unsafe { let mut slice = slice::from_raw_parts_mut(buffers, n); for index in 0..n { slice[index] = index; } } } 複製程式碼
進一步,可使用Rust類似C#的foreach進行迴圈,同時縮小unsafe程式碼塊的影響空間。讀指標也可用這個方案。
use std::slice; #[no_mangle] pub extern "C" fn write_to_c_buffers(n: usize, buffers: *mut i32) { let mut buffers = unsafe { slice::from_raw_parts_mut(buffers, n) }; for slice in buffers { *slice = //do something; } } 複製程式碼
讀指標
int summary(size_t count, int ptr*) { int sum = 0; for (int i = 0; i < count; ++i) { sum += ptr[i]; } return sum; } 複製程式碼
將ptr
傳遞到Rust進行求和,用slice可以直接操作,避免分配額外的記憶體,示例如下:
use std::slice; #[no_mangle] pub extern "C" fn summary_for_c_buffers(n: usize, buffers: *const i32) { unsafe { let slice = slice::from_raw_parts(buffers, n); let mut sum = 0; for index in 0..n { sum += slice[index]; } sum } } 複製程式碼