首页 > 解决方案 > 使用 Box::from_raw() Box::into_raw() 往返 Rust 无效指针

问题描述

在尝试创建一些 FFI 帮助程序代码时,我正在努力反对 Box 的这种所谓的简单用法。

free(): invalid pointer当与具有字段的结构一起使用时,此处的示例似乎给出了错误。

pub struct Handle(usize);

impl Handle {
    pub fn from<T>(obj: T) -> Self {
        let boxed = Box::new(obj);
        let mut ptr = Box::into_raw(boxed);
        Self::from_ptr_mut(&mut ptr)
    }

    pub fn from_ptr_mut<T>(ptr: &mut T) -> Self {
        Self(ptr as *mut T as usize)
    }

    pub fn to_box<T>(self) -> Box<T> {
        let obj: *mut T = self.to_ptr_mut();
        unsafe { Box::from_raw(obj) }
    }

    pub fn to_ptr_mut<T>(self) -> *mut T {
        self.0 as *mut T
    }
}

#[allow(dead_code)]
struct Crashes { value: u64 }

impl Drop for Crashes {
    fn drop(&mut self) {
        println!("Crashes dropped");
    }
}

fn crashes() {
    let t = Crashes { value: 12 };
    let a = Handle::from(t);
    let b = a.to_box::<Crashes>();
    drop(b);
}

struct Works;

impl Drop for Works {
    fn drop(&mut self) {
        println!("Works dropped");
    }
}

fn works() {
    let t = Works;
    let a = Handle::from(t);
    let b = a.to_box::<Works>();
    drop(b);
}

fn main() {
    works();
    crashes();
}

您可以将其粘贴到https://play.rust-lang.org/并查看它如何因错误而引发中止free(): invalid pointer

似乎在适当的时候调用了 drop 函数,但是指针似乎在某种程度上无效

标签: memoryrustffi

解决方案


你最终在这里创建了一个双指针:

impl Handle {
    pub fn from<T>(obj: T) -> Self {
        let boxed = Box::new(obj);
        let mut ptr = Box::into_raw(boxed);
        Self::from_ptr_mut(&mut ptr)
    }

    pub fn from_ptr_mut<T>(ptr: &mut T) -> Self {
        Self(ptr as *mut T as usize)
    }
    ...
}

Box::into_raw返回一个指针,但随后您对该指针进行可变引用,并将该地址存储为usize. 您应该只使用*mut Tas 返回的Box::into_raw.

编译带有双指针的非工作代码的原因是 yourfrom<T>和 yourfrom_ptr_mut<T>可以采用完全不同的T参数。如果我们认为T传递给的类型from<T>是具体类型,那么在这种情况下,您将使用 type 的参数调用from_ptr_mut<U>(where Uis ) 。*mut T&mut *mut T

它应该是这样的:

impl Handle {
    pub fn from<T>(obj: T) -> Self {
        let boxed = Box::new(obj);
        let ptr = Box::into_raw(boxed);
        Self::from_ptr_mut(ptr)
    }

    pub fn from_ptr_mut<T>(ptr: *mut T) -> Self {
        Self(ptr as usize)
    }
    ...
}

操场上的工作示例。


即使我们处于您的领域,也可以通过将参数绑定到您的结构来unsafe让编译器为您完成一些工作。这样,您将被静态阻止加载与存储不同的类型。THandle

Handle 包含 PhantomData 的 Playground 示例。

在第二个示例中,您不必告诉编译器您正在检索 la 哪个项目a.to_box::<Crashes>(),这很好,因为您不能通过指定错误的类型来引入未定义的行为。


推荐阅读