Module alloc::boxed

1.0.0 · source ·
Expand description

用于堆分配的 Box<T> 类型。

Box<T>,简称为 ‘box’,在 Rust 中提供了最简单的堆分配形式。Boxes 为这个分配提供所有权,并在离开作用域时丢弃它们的内容。Boxes 还确保它们分配的字节数永远不会超过 isize::MAX 字节。

Examples

通过创建 Box,将值从栈移动到堆:

let val: u8 = 5;
let boxed: Box<u8> = Box::new(val);
Run

通过 解引用 将值从 Box 移回栈:

let boxed: Box<u8> = Box::new(5);
let val: u8 = *boxed;
Run

创建递归数据结构:

#[derive(Debug)]
enum List<T> {
    Cons(T, Box<List<T>>),
    Nil,
}

let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("{list:?}");
Run

这将打印 Cons(1, Cons(2, Nil))

递归结构必须为 boxed,因为如果 Cons 的定义如下所示:

Cons(T, List<T>),
Run

这是行不通的。这是因为 List 的大小取决于列表中有多少个元素,因此我们不知道为 Cons 分配多少内存。通过引入具有定义大小的 Box<T>,我们知道 Cons 的大小。

内存布局

对于非零大小的值,Box 将使用 Global 分配器进行分配。假定与分配器一起使用的 Layout 对于该类型是正确的,则在 Box 和使用 Global 分配器分配的裸指针之间进行双向转换是有效的。

更准确地说,已使用 Layout::for_value(&*value)Global 分配器一起分配的 value: *mut T 可以使用 Box::<T>::from_raw(value) 转换为 box。 相反,可以使用带有 Layout::for_value(&*value)Global 分配器重新分配支持从 Box::<T>::into_raw 获得的 value: *mut T 的内存。

对于零大小的值,Box 指针对于读取和写入仍必须为 有效的,并且必须充分对齐。 特别是,将任何对齐的非零整数字面量强制转换为裸指针都会产生有效的指针,但是指向先前分配的内存 (由于释放后的指针) 的指针无效。 如果不能使用 Box::new,建议将 Box 生成到 ZST 的推荐方法是使用 ptr::NonNull::dangling

只要 T: Sized,就可以保证将 Box<T> 表示为单个指针,并且还与 C 指针 ABI 兼容 (即 C 类型 T*)。 这意味着,如果您有从 C 调用的外部 “C” Rust 函数,则可以使用 Box<T> 类型定义那些 Rust 函数,并在 C 侧使用 T* 作为对应类型。 例如,考虑下面的 C 头文件,该标头声明创建和销毁某种 Foo 值的函数:

/* C 头文件 */

/* 将所有权归还给调用者 */
struct Foo* foo_new(void);

/* 从调用者那里获得所有权; 使用 null 调用时无操作 */
void foo_delete(struct Foo*);

这两个函数可以在 Rust 中实现,如下所示。在这里,来自 C 的 struct Foo* 类型被转换为 Box<Foo>,它捕获了所有权约束。 还要注意,由于 Box<Foo> 不能为 null,因此 foo_delete 的 nullable 参数在 Rust 中表示为 Option<Box<Foo>>

#[repr(C)]
pub struct Foo;

#[no_mangle]
pub extern "C" fn foo_new() -> Box<Foo> {
    Box::new(Foo)
}

#[no_mangle]
pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}
Run

即使 Box<T> 具有与 C 指针相同的表示形式和 C ABI,但这并不意味着您可以将任意 T* 转换为 Box<T> 并期望一切正常。 Box<T> 值将始终是完全对齐的非空指针。此外,Box<T> 的析构函数将尝试使用分配器释放该值。通常,最佳实践是仅对来自分配器的指针使用 Box<T>

重要:至少目前,对于在 C 语言中定义但从 Rust 中调用的函数,应该避免使用 Box<T> 类型。在这些情况下,您应该尽可能直接地镜像 C 类型。 如 rust-lang/unsafe-code-guidelines#198 中所述,使用 C 定义仅使用 T*Box<T> 这样的类型可能导致未定义的行为。

不安全代码的注意事项

警告: 此部分不规范,可能会更改,将来可能会放宽! 它是当前在编译器中实现的规则的简化总结。

Box<T> 的别名规则与 &mut T 相同。Box<T> 断言其内容的唯一性。不允许在 box 被可变的、移动或借用后使用从 box 派生的裸指针,因为 &mut T 是不允许的。 有关从不安全代码中使用 box 的更多指导,请参见 rust-lang/unsafe-code-guidelines#326

Structs

  • ThinBoxExperimental
    ThinBox.
  • 唯一拥有 T 类型堆分配的指针类型。