gpt4 book ai didi

rust - 如何处理可以拥有或借用的 FFI 未确定大小的类型?

转载 作者:行者123 更新时间:2023-11-29 08:00:27 25 4
gpt4 key购买 nike

c_strange_t是一种不透明的 C 类型,只能在指针后面看到。包装此类型时,有时我们有责任使用 c_free_strange_t(*c_strange_t) 释放内存,而其他时候我们不负责释放数据,我们只负责准确控制生命周期。

如果这种类型可以映射到 Rust 中的 2 种类型,这两种类型的工作方式与 str 类似,那将是符合人体工程学的。和 String , 哪里有 impl Deref<Target=str> for String .借用的类型需要标记为仅在引用后有效。

这可能吗,如何实现?

最佳答案

这似乎可行,但它确实需要使用一个小的 unsafe block ,因此您应该在 Miri 和 Valgrind 等常规工具下进行测试。这里做的主要假设1c_void不能正常构造。 #[repr(transparent)]用于确保 FooBorrowed 新类型与 c_void 具有相同的内存布局。一切都应该以“只是一个指针”结束:

use std::{ffi::c_void, mem, ops::Deref};

#[repr(transparent)]
struct FooBorrowed(c_void);
struct FooOwned(*mut c_void);

fn fake_foo_new(v: u8) -> *mut c_void {
println!("C new called");
Box::into_raw(Box::new(v)) as *mut c_void
}

fn fake_foo_free(p: *mut c_void) {
println!("C free called");
let p = p as *mut u8;
if !p.is_null() {
unsafe { Box::from_raw(p) };
}
}

fn fake_foo_value(p: *const c_void) -> u8 {
println!("C value called");
let p = p as *const u8;
unsafe {
p.as_ref().map_or(255, |p| *p)
}
}

impl FooBorrowed {
fn value(&self) -> u8 {
fake_foo_value(&self.0)
}
}

impl FooOwned {
fn new(v: u8) -> FooOwned {
FooOwned(fake_foo_new(v))
}
}

impl Deref for FooOwned {
type Target = FooBorrowed;

fn deref(&self) -> &Self::Target {
unsafe { mem::transmute(self.0) }
}
}

impl Drop for FooOwned {
fn drop(&mut self) {
fake_foo_free(self.0)
}
}

fn use_it(foo: &FooBorrowed) {
println!("{}", foo.value())
}

fn main() {
let f = FooOwned::new(42);
use_it(&f);
}

如果 C 库实际上给你一个指针,你需要做更多的不安全:

fn fake_foo_borrowed() -> *const c_void {
println!("C borrow called");
static VALUE_OWNED_ELSEWHERE: u8 = 99;
&VALUE_OWNED_ELSEWHERE as *const u8 as *const c_void
}

impl FooBorrowed {
unsafe fn new<'a>(p: *const c_void) -> &'a FooBorrowed {
mem::transmute(p)
}
}

fn main() {
let f2 = unsafe { FooBorrowed::new(fake_foo_borrowed()) };
use_it(f2);
}

正如您所指出的,FooBorrowed::new 返回一个生命周期不受限制的引用;这很危险。在许多情况下,您可以构造一个较小的作用域并使用提供生命周期的东西:

impl FooBorrowed {
unsafe fn new<'a>(p: &'a *const c_void) -> &'a FooBorrowed {
mem::transmute(*p)
}
}

fn main() {
let p = fake_foo_borrowed();
let f2 = unsafe { FooBorrowed::new(&p) };
use_it(f2);
}

这可以防止您在指针变量有效时使用引用,这保证是真正的生命周期,但在许多情况下“足够接近”。太短不要太长更重要!


1 — 在 Rust 的 future 版本中,您应该使用外部类型来创建有保证的不透明类型:

extern "C" {
type my_opaque_t;
}

关于rust - 如何处理可以拥有或借用的 FFI 未确定大小的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42613798/

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