Rust代码示例——19.1.Box,堆和栈(Box,stack,heap)
摘要:Rust默认在栈上分配值,使用Box<T>可在堆上分配。Box是智能指针,离开作用域自动释放内存。通过*操作符可解引用获取堆值。代码示例展示了Point和Rectangle结构体在栈/堆的分配方式,使用mem::size_of_val()比较内存占用情况:栈变量直接存储值,Box只存储指针(64位系统占8字节)。当Point实现Copy时,解引用会复制值到栈;未实现则移动所有权。
在Rust中所有的值默认都是在栈上进行分配的。值也可以通过创建Box<T>在堆上进行分配。一个Box是一个智能指针,它指向T类型的值在堆上分配地址。当box离开作用域,就会调用它的销毁函数。它指向的内部对象也会被销毁,堆上分配的内存就被释放。
Box包含的值可以使用*操作符进行解引用;这会移除中间层。
use std::mem;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
// A Rectangle can be specified by where its top left and bottom right
// corners are in space
#[allow(dead_code)]
struct Rectangle {
top_left: Point,
bottom_right: Point,
}
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
fn boxed_origin() -> Box<Point> {
// Allocate this point on the heap, and return a pointer to it
Box::new(Point { x: 0.0, y: 0.0 })
}
fn main() {
// (all the type annotations are superfluous)
// Stack allocated variables
let point: Point = origin();
let rectangle: Rectangle = Rectangle {
top_left: origin(),
bottom_right: Point { x: 3.0, y: -4.0 }
};
// Heap allocated rectangle
let boxed_rectangle: Box<Rectangle> = Box::new(Rectangle {
top_left: origin(),
bottom_right: Point { x: 3.0, y: -4.0 },
});
// The output of functions can be boxed
let boxed_point: Box<Point> = Box::new(origin());
// Double indirection
let box_in_a_box: Box<Box<Point>> = Box::new(boxed_origin());
println!("Point occupies {} bytes on the stack",
mem::size_of_val(&point));
println!("Rectangle occupies {} bytes on the stack",
mem::size_of_val(&rectangle));
// box size == pointer size
println!("Boxed point occupies {} bytes on the stack",
mem::size_of_val(&boxed_point));
println!("Boxed rectangle occupies {} bytes on the stack",
mem::size_of_val(&boxed_rectangle));
println!("Boxed box occupies {} bytes on the stack",
mem::size_of_val(&box_in_a_box));
// Copy the data contained in `boxed_point` into `unboxed_point`
let unboxed_point: Point = *boxed_point;
println!("Unboxed point occupies {} bytes on the stack",
mem::size_of_val(&unboxed_point));
}
代码解析
这段代码主要演示了Rust中栈的分配和对分配(通过Box<T>)的基本概念和内存占用情况。
栈分配
默认行为:像Point和rectangle这样的局部变量都是直接在栈上分配
栈分配速度快,但是声明周期通常与作用域绑定,空间有限。
堆分配
使用Box::new(value)可以将值value移动到堆上分配,并返回一个智能指针Box<T>,这个指针本身存储在栈上。
Box<T>是一个智能指针,它拥有其所指向的堆数据的所有权。
当Box<T>离开了作用域时,其Drop接口会被调用,负责释放堆上的内存,防止内存泄漏。
代码中创建了boxed_rectangle、boxed_point和box_in_a_box。
内存占用
mem::size_of_val(&val)用户获取值val在栈上所占用的字节数。
对于直接存储在栈上的类型(如point和rectangle),他返回该类型的大小。
Point(x:f64,y:f64):两个f64(每个8个字节),通常是16字节。
Rectangle{top_left:Point,bottom_right:Point}:两个Point,通常是32个字节。
对于Box<T>类型的变量,存储在栈上的这两个指针本身(通常是一个指针,在加上一些元数据),而不是整个T对象。在大多数系统上,指针的大小通常是8个字节(64位系统)。因此打印boxed_point、boxed_rectangle和box_in_a_box的大小都会是8个字节(指向堆数据的指针的大小),而不管它们指向的Point和Rectangle实际有多大。
box_in_a_box:Box<Box<Point>>的大小也是8个字节,因为它仍然是一个指针(执行另一个在堆上的Box<Point>)。
解引用
使用*操作符可以解引用Box<T>,获取其拥有的堆上的值。
let unboxed_point: Point = *boxed_point;这一行:
*boxed_point解引用boxed_point,得到它拥有的堆上的Point。
由于Point实现了Copy接口,这里会发生复制(copy),将堆上的Point数据复制一份到栈上的unboxed_point变量中。
如果Point没有实现copy,那么这行代码会发生移动(move),所有权会从堆转移到栈上的变量,之后boxed_point将不可再用。
总结
这段代码的核心目的是:
- 展示Rust中默认栈分配和现实堆分配(通过Box)的基本用法。
- 阐明Box<T>智能指针的本质:它是一个存储在栈上的指针,指向堆上分配的数据,并负责该数据的生命周期管理(自动释放)。
- 对比内存占用:直观地展示了直接栈分配的值和指向堆数据的Box指针在栈空间占用上的区别(后者为固定大小的指针)。
- 演示如何通过解引用操作符*来访问Box内部的值,并可能将其移出或复制到栈上。
Box在Rust中是最基本的智能指针,常用于一下场景:当你有一个其大小无法再编译时确定的数据(例如递归类型的数据),或者当你希望拥有一个大型数据避免在栈上拷贝时,或者当你需要实现一个特定trait类型的存在时(dyn trait)。
更多推荐



所有评论(0)