在 Rust 中的 `v : T` 中,`Layout::from_value(&v)` 和 `Layout::new::<T>` 可以不同吗?

问题描述 投票:0回答:1

我正在从标准库中实现我自己的

Box<T>
以学习如何编写不安全的代码。

我的实现看起来像:

use std::marker::PhantomData;
use std::{alloc, mem, ptr};
use ptr::NonNull;
use alloc::Layout;

pub struct MyBox<T> {
    data: NonNull<T>,
    _marker: PhantomData<T>,
}

impl<T> MyBox<T> {
    pub fn new(data: T) -> MyBox<T> {
        unsafe {
            let layout = Layout::for_value(&data);
            let ptr = alloc::alloc(layout) as *mut T;
            ptr::write(ptr, data);
            let ptr = NonNull::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout));
            MyBox {
                data: ptr,
                _marker: PhantomData,
            }
        }
    }
}

impl<T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe {
            let ptr = self.data.as_ptr();
            drop(ptr::read(ptr));
            alloc::dealloc(ptr as *mut u8, Layout::new::<T>())
        }
    }
}

如您所见,我使用了不同的方法来获取布局,但是,

dealloc
要求我们使用与分配值相同的布局。

根据这些方法的源代码,

Layout::for_value
使用
mem::size_of_val
mem::align_of_val
,而
Layout::new
使用
mem::size_of
mem::align_of
。据我从文档中掌握的信息,只有当 T 是动态大小类型 (DST) 时它们才会不同。因此,就我仅针对大小类型实施
MyBox
(除非明确指定,否则它们实际上是)我应该没问题,不是吗?

rust unsafe
1个回答
1
投票

来自

std::mem::size_of_val
的文档:

这通常与

size_of::<T>()
相同。然而,当
T
没有 静态已知大小,例如切片 [
[T]
][slice] 或 [trait object], 然后
size_of_val
可用于获取动态已知的大小。

默认情况下,当您引入类型参数

T
时,就像您在
MyBox<T>
中所做的那样,会有一个隐含的
T: Sized
约束。这意味着
mem::size_of_val
mem::size_of
将返回相同的东西。

为了支持未调整大小的

T
,你需要明确说明:

pub struct MyBox<T> where T: ?Sized {
  // ...
}

和:

impl<T> Drop for MyBox<T> where T: ?Sized {
    fn drop(&mut self) {
        // ...
    }
}

请注意,您的

new
函数仍然需要具有
T: Sized
,因为它需要按值接受它作为参数。但是,您可能会找到其他方法来为 DST 构建
MyBox

随着这个变化,你现在不能使用

Layout::new<T>()
,因为它需要
T: Sized
。相反,您需要使用
Layout
构建
for_value
,它没有
Sized
约束。

pub struct MyBox<T: ?Sized> {
    data: NonNull<T>,
    _marker: PhantomData<T>,
}

impl<T: ?Sized> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe {
            let ptr = self.data.as_ptr();
            let layout = Layout::for_value(unsafe { &*ptr } );
            ptr.drop_in_place();
            alloc::dealloc(ptr as *mut u8, layout)
        }
    }
}

您还需要将

drop(ptr::read(ptr));
更改为
ptr.drop_in_place()
- 部分是因为它更好,但 主要是 因为
drop
也需要
T: Sized
:)

© www.soinside.com 2019 - 2024. All rights reserved.