Box::from_raw() Box::into_raw() 往返的 Rust 无效指针
Rust invalid pointer with Box::from_raw() Box::into_raw() round trip
我在尝试创建一些 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 函数,但指针似乎不知何故无效
你最终在这里创建了一个双指针:
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
returns 一个指针,但随后您获取该指针的可变引用,并将该地址存储为 usize
。您应该只使用 Box::into_raw
.
返回的 *mut T
编译带有双指针的非工作代码的原因是您的 from<T>
和 from_ptr_mut<T>
可以采用完全不同的 T
参数。如果我们认为传递给 from<T>
的类型 T
是一个具体类型,那么在这种情况下,您正在调用 from_ptr_mut<U>
(其中 U
是 *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)
}
...
}
Working example in the playground.
即使我们处于 unsafe
领域,您也可以让编译器为您完成一些工作,方法是将参数 T
绑定到您的 Handle
结构。这样,您将被静态阻止加载与存储不同的类型。
Playground example where Handle includes a PhantomData.
在第二个示例中,您不必告诉编译器您正在检索哪个项目 a.to_box::<Crashes>()
,这很好,因为您不能通过指定错误的类型来引入未定义的行为。
我在尝试创建一些 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 函数,但指针似乎不知何故无效
你最终在这里创建了一个双指针:
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
returns 一个指针,但随后您获取该指针的可变引用,并将该地址存储为 usize
。您应该只使用 Box::into_raw
.
*mut T
编译带有双指针的非工作代码的原因是您的 from<T>
和 from_ptr_mut<T>
可以采用完全不同的 T
参数。如果我们认为传递给 from<T>
的类型 T
是一个具体类型,那么在这种情况下,您正在调用 from_ptr_mut<U>
(其中 U
是 *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)
}
...
}
Working example in the playground.
即使我们处于 unsafe
领域,您也可以让编译器为您完成一些工作,方法是将参数 T
绑定到您的 Handle
结构。这样,您将被静态阻止加载与存储不同的类型。
Playground example where Handle includes a PhantomData.
在第二个示例中,您不必告诉编译器您正在检索哪个项目 a.to_box::<Crashes>()
,这很好,因为您不能通过指定错误的类型来引入未定义的行为。