box关键字有什么作用?
在 Rust 中,我们可以使用 Box
类型在堆上分配东西.此类型用于安全地抽象指向堆内存的指针.Box
由 Rust 标准库提供.
In Rust, we can use the Box<T>
type to allocate things on the heap. This type is used to safely abstract pointers to heap memory. Box<T>
is provided by the Rust standard library.
我很好奇Box<T>
分配是如何实现的,所以我找到了其源代码.这是 Box
的代码(从 Rust 1.0 开始):
I was curious about how Box<T>
allocation is implemented, so I found its source code. Here is the code for Box<T>::new
(as of Rust 1.0):
impl<T> Box<T> {
/// Allocates memory on the heap and then moves `x` into it.
/// [...]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline(always)]
pub fn new(x: T) -> Box<T> {
box x
}
}
实现中唯一的一行返回值 box x
.这个box
关键字在官方文档中没有任何解释;实际上它只是在 std::boxed
文档页面.
The only line in the implementation returns the value box x
. This box
keyword is not explained anywhere in the official documentation; in fact it is only mentioned briefly on the std::boxed
documentation page.
注意: 这个回复有点旧.由于它讨论了内部结构和不稳定的功能,因此事情发生了一些变化.但基本机制保持不变,所以答案仍然能够解释box
的潜在机制.
NOTE: This reply is a bit old. Since it talks about internals and unstable features, things have changed a little bit. The basic mechanism remains the same though, so the answer is still capable of explaining the undelying mechanisms of box
.
box x
通常用什么来分配和释放内存?
What does box x
usually uses to allocate and free memory?
答案是标有 lang 项 exchange_malloc
用于分配和 exchange_free
用于释放的函数.您可以在 heap.rs#L112 和 heap.rs#L125.
The answer is the functions marked with lang items exchange_malloc
for allocation and exchange_free
for freeing. You can see the implementation of those in the default standard library at heap.rs#L112 and heap.rs#L125.
最后,box x
语法取决于以下语言项:
In the end the box x
syntax depends on the following lang items:
-
owned_box
在Box
结构上封装分配的指针.该结构体不需要Drop
实现,它由编译器自动实现. -
exchange_malloc
分配内存. -
exchange_free
释放之前分配的内存.
-
owned_box
on aBox
struct to encapsulate the allocated pointer. This struct does not need aDrop
implementation, it is implemented automatically by the compiler. -
exchange_malloc
to allocate the memory. -
exchange_free
to free the previously allocated memory.
这可以在 lang items 使用这个 no_std
示例的不稳定锈书的章节:
This can be effectively seen in the lang items chapter of the unstable rust book using this no_std
example:
#![feature(lang_items, box_syntax, start, no_std, libc)]
#![no_std]
extern crate libc;
extern {
fn abort() -> !;
}
#[lang = "owned_box"]
pub struct Box<T>(*mut T);
#[lang = "exchange_malloc"]
unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
let p = libc::malloc(size as libc::size_t) as *mut u8;
// malloc failed
if p as usize == 0 {
abort();
}
p
}
#[lang = "exchange_free"]
unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) {
libc::free(ptr as *mut libc::c_void)
}
#[start]
fn main(argc: isize, argv: *const *const u8) -> isize {
let x = box 1;
0
}
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
注意 Drop
是如何没有为 Box
结构实现的?好吧,让我们看看为 main
生成的 LLVM IR:
Notice how Drop
was not implemented for the Box
struct? Well let's see the LLVM IR generated for main
:
define internal i64 @_ZN4main20hbd13b522fdb5b7d4ebaE(i64, i8**) unnamed_addr #1 {
entry-block:
%argc = alloca i64
%argv = alloca i8**
%x = alloca i32*
store i64 %0, i64* %argc, align 8
store i8** %1, i8*** %argv, align 8
%2 = call i8* @_ZN8allocate20hf9df30890c435d76naaE(i64 4, i64 4)
%3 = bitcast i8* %2 to i32*
store i32 1, i32* %3, align 4
store i32* %3, i32** %x, align 8
call void @"_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE"(i32** %x)
ret i64 0
}
allocate
(_ZN8allocate20hf9df30890c435d76naaE
) 按预期调用以构建 Box
,同时...看!Box
(_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE
) 的 Drop
方法!让我们看看这个方法的 IR:
The allocate
(_ZN8allocate20hf9df30890c435d76naaE
) was called as expected to build the Box
, meanwhile... Look! A Drop
method for the Box
(_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE
)! Let's see the IR for this method:
define internal void @"_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE"(i32**) unnamed_addr #0 {
entry-block:
%1 = load i32** %0
%2 = ptrtoint i32* %1 to i64
%3 = icmp ne i64 %2, 2097865012304223517
br i1 %3, label %cond, label %next
next: ; preds = %cond, %entry- block
ret void
cond: ; preds = %entry-block
%4 = bitcast i32* %1 to i8*
call void @_ZN10deallocate20he2bff5e01707ad50VaaE(i8* %4, i64 4, i64 4)
br label %next
}
就是这样,deallocate
(ZN10deallocate20he2bff5e01707ad50VaaE
) 在编译器生成的 Drop 上被调用!
There it is, deallocate
(ZN10deallocate20he2bff5e01707ad50VaaE
) being called on the compiler generated Drop!
注意标准库Drop
特性不是由用户代码实现的.事实上,Box
是一个有点神奇的结构.
Notice even on the standard library the Drop
trait is not implemented by user-code. Indeed Box
is a bit of a magical struct.