gpt4 book ai didi

rust - 如果ffi函数修改了指针,那么拥有的结构是否应该被引用为可变的?

转载 作者:行者123 更新时间:2023-12-03 11:44:27 24 4
gpt4 key购买 nike

我目前正在尝试Rust的FFI功能,并使用libcurl作为练习来实现了一个简单的HTTP请求。考虑以下独立的示例:

use std::ffi::c_void;

#[repr(C)]
struct CURL {
_private: [u8; 0],
}

// Global CURL codes
const CURL_GLOBAL_DEFAULT: i64 = 3;
const CURLOPT_WRITEDATA: i64 = 10001;
const CURLOPT_URL: i64 = 10002;
const CURLOPT_WRITEFUNCTION: i64 = 20011;

// Curl types
type CURLcode = i64;
type CURLoption = i64;

// Curl function bindings
#[link(name = "curl")]
extern "C" {
fn curl_easy_init() -> *mut CURL;
fn curl_easy_setopt(handle: *mut CURL, option: CURLoption, value: *mut c_void) -> CURLcode;
fn curl_easy_perform(handle: *mut CURL) -> CURLcode;
fn curl_global_init(flags: i64) -> CURLcode;
}

// Curl callback for data retrieving
extern "C" fn callback_writefunction(
data: *mut u8,
size: usize,
nmemb: usize,
user_data: *mut c_void,
) -> usize {
let slice = unsafe { std::slice::from_raw_parts(data, size * nmemb) };

let mut vec = unsafe { Box::from_raw(user_data as *mut Vec<u8>) };
vec.extend_from_slice(slice);
Box::into_raw(vec);
nmemb * size
}

type Result<T> = std::result::Result<T, CURLcode>;

// Our own curl handle
pub struct Curl {
handle: *mut CURL,
data_ptr: *mut Vec<u8>,
}

impl Curl {
pub fn new() -> std::result::Result<Curl, CURLcode> {
let ret = unsafe { curl_global_init(CURL_GLOBAL_DEFAULT) };
if ret != 0 {
return Err(ret);
}

let handle = unsafe { curl_easy_init() };
if handle.is_null() {
return Err(2); // CURLE_FAILED_INIT according to libcurl-errors(3)
}

// Set data callback
let ret = unsafe {
curl_easy_setopt(
handle,
CURLOPT_WRITEFUNCTION,
callback_writefunction as *mut c_void,
)
};
if ret != 0 {
return Err(2);
}

// Set data pointer
let data_buf = Box::new(Vec::new());
let data_ptr = Box::into_raw(data_buf);
let ret = unsafe {
curl_easy_setopt(handle, CURLOPT_WRITEDATA, data_ptr as *mut std::ffi::c_void)
};
match ret {
0 => Ok(Curl { handle, data_ptr }),
_ => Err(2),
}
}

pub fn set_url(&self, url: &str) -> Result<()> {
let url_cstr = std::ffi::CString::new(url.as_bytes()).unwrap();
let ret = unsafe {
curl_easy_setopt(
self.handle,
CURLOPT_URL,
url_cstr.as_ptr() as *mut std::ffi::c_void,
)
};
match ret {
0 => Ok(()),
x => Err(x),
}
}

pub fn perform(&self) -> Result<String> {
let ret = unsafe { curl_easy_perform(self.handle) };
if ret == 0 {
let b = unsafe { Box::from_raw(self.data_ptr) };
let data = (*b).clone();
Box::into_raw(b);
Ok(String::from_utf8(data).unwrap())
} else {
Err(ret)
}
}
}

fn main() -> Result<()> {
let my_curl = Curl::new().unwrap();
my_curl.set_url("https://www.example.com")?;
my_curl.perform().and_then(|data| Ok(println!("{}", data)))
// No cleanup code in this example for the sake of brevity.
}
在执行此操作的同时,我感到惊讶的是,无需将 my_curl声明为 mut,因为即使这些方法都将 &mut self指针传递给FFI函数,也没有使用 mut*s。
由于内部缓冲区已修改,我是否应该将 perform的声明更改为使用 &mut self而不是 &self(为了安全)? Rust不会强制执行此操作,但是Rust当然不知道libcurl会修改缓冲区。
这个小例子运行良好,但是我不确定在大型程序中是否会遇到任何类型的问题,此时编译器可能会优化 Curl结构上的非可变访问,即使该结构的实例已被修改-或至少指针所指向的数据。

最佳答案

与普遍的看法相反,Rust在传递*const/*mut指针方面绝对没有借位检查器引起的限制。不需要,因为取消引用指针本质上是不安全的,并且只能在此类块中完成,而程序员需要手动验证所有必要的不变式。就您的情况而言,您已经怀疑了,您需要告诉编译器这是一个可变的引用。
有兴趣的读者一定要阅读ffi section of the nomicon,以找到一些有趣的方法来用它射击自己。

关于rust - 如果ffi函数修改了指针,那么拥有的结构是否应该被引用为可变的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64668176/

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