【问题标题】:How to get mutability errors for FFI wrapper如何获取 FFI 包装器的可变性错误
【发布时间】:2022-06-15 08:56:17
【问题描述】:

我正在与一个 FFI c 库进行交互,编写一个“胖”层来包含所有不安全的代码。我注意到,如果我通过 *mut 类型改变数据,我不会收到任何警告。

我希望类型检查器在调用 FFI 之前强制我拥有结构的正确所有权。 C API 更改可能会出现问题

pub fn c_thing_mutate(thing: *const c_thing);

pub fn c_thing_mutate(thing: *mut c_thing);

Rust 不会警告 rust API 现在需要更改。

导致 UB 的示例代码。 c_thing_mutate 对源自&self 的数据进行变异:

use std::ptr::null_mut;

#[repr(C)]
pub struct c_thing {
    _unused: [u8; 0],
}

extern "C" {
    pub fn c_thing_init(thing: *mut *mut c_thing);
    pub fn c_thing_mutate(thing: *mut c_thing);
    pub fn c_thing_release(thing: *mut c_thing);
}

struct CThingWrapper {
    thing: *mut c_thing,
}

impl CThingWrapper {
    pub fn new() -> CThingWrapper {
        let mut thing: *mut c_thing = null_mut();
        unsafe { c_thing_init(&mut thing) };
        CThingWrapper { thing }
    }

    pub fn mutate(&self) {
        unsafe { c_thing_mutate(self.thing) };
    }
}

impl Drop for CThingWrapper {
    fn drop(&mut self) {
        unsafe {
            c_thing_release(self.thing);
        }
    }
}

fn main() {
    let x = CThingWrapper::new();
    x.mutate();
}

我认为 Box 或 Cell 可能会帮助我解决这个问题。 Box 很难,因为我无法放下东西:

use std::ptr::null_mut;

#[repr(C)]
pub struct c_thing {
    _unused: [u8; 0],
}

extern "C" {
    pub fn c_thing_init(thing: *mut *mut c_thing);
    pub fn c_thing_mutate(thing: *mut c_thing);
    pub fn c_thing_release(thing: *mut c_thing);
}

struct CThingWrapper {
    thing: Box<c_thing>,
}

impl CThingWrapper {
    pub fn new() -> CThingWrapper {
        let mut thing: *mut c_thing = null_mut();
        unsafe { c_thing_init(&mut thing) };
        CThingWrapper {
            thing: unsafe { Box::from_raw(thing) },
        }
    }

    pub fn mutate(&mut self) {
        let thing = self.thing.as_mut();
        unsafe {
            c_thing_mutate(thing);
            self.thing = Box::from_raw(thing)
        };
    }
}

impl Drop for CThingWrapper {
    fn drop(&mut self) {
        unsafe {
            let thing = Box::leak(self.thing);
            c_thing_release(thing);
        }
    }
}

fn main() {
    let x = CThingWrapper::new();
    x.mutate();
}

错误:“无法移出self.thing,它位于可变变量后面 引用,移动发生是因为self.thing 的类型为Box&lt;c_thing&gt;,它没有实现Copy trait”

Box 似乎不太正确,Box 想要分配和释放内存,但我需要将其委托给 C API。

单元格不完全正确; “Cell 通过将值移入和移出 Cell 来实现内部可变性。”。

我怀疑我可能需要一个类型的组合,例如Option&lt;Box&lt;T&gt;&gt;

【问题讨论】:

  • 第一个代码不是UB。你持有一个指针,即使通过&amp;self 也可以对其进行变异(实际上,指针是外部可变性)。
  • 另外,borrowck 在这里不相关,这是关于类型检查的。
  • 另外,请说明您遇到的所有“问题”,来自cargo check的全部错误。
  • @ChayimFriedman 谢谢,也许我误解了这一点:“变异不可变数据。const 项内的所有数据都是不可变的。此外,通过共享引用或不可变绑定拥有的数据获得的所有数据都是不可变的,除非该数据包含在 UnsafeCell 中。”它特别提到了 UnsafeCell,但没有提到 *mut 作为改变数据的有效方式。
  • 这确实有点误导。它指的是连续数据,而不是通过原始指针到达的数据。

标签: rust ffi


【解决方案1】:

这个包装器创建了我想要的语义,只有&amp;mut CPtr&lt;T&gt; 可以访问*mut T,而&amp; CPtr&lt;T&gt; 可以使用*const T

/// This struct exists only to force *mut to be used in a mutable context
/// For example, to prevent this:
///
/// fn mutate(ptr: *mut T) {}
/// fn (&self) {
///     mutate(self.ptr)
/// }
///
#[derive(Debug)]
pub struct CPtr<T> {
    ptr: *mut T,
}

impl<T> CPtr<T> {
    pub fn new(ptr: *mut T) -> Self {
        Self { ptr }
    }

    pub fn as_ptr(&self) -> *const T {
        self.ptr
    }

    pub fn as_mut_ptr(&mut self) -> *mut T {
        self.ptr
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多