rust为什么要有box?

百度上搜索到的内容说得不太明白。
说box上指向的数据在堆,so what?
box被清掉的时候也会自动清掉指向的数据,那又如何?这个数据放在栈上不一样么?我才不在乎你在堆还是栈。除非有什么场境必须用box才能解决问题,没它不行。

阅读 3.5k
5 个回答

如果从 C 语言的角度来解释,int 数组(栈)和 int 指针(堆)有什么差别呢,就在于 int 数组会在当前作用域结束时自动释放,而 int 指针是手动控制的。

// ???
int[] getArr() {
    // ...
}

// Ok
int *getArr() {
    // ...
}

那好,回到 Rust 角度,好像没有这个问题,完全可以返回数组了,可是有没有发现的是,Rust 当中的数组长度只能是常量,这是绝对固定的,这样才能方便管理栈内存。不能固定的统一使用 Vec

在 Rust 当中存在一个说法:当你理解如何在 Rust 当中编写链表后,才能算入门成功。

pub struct ListNode {
    pub val: i32,
    pub next: Option<Box<ListNode>>,
}

如果去掉 <Box>,编译器也会给出说明:

recursive type ListNode has infinite size

也就是说:所有不能在编译时确定空间大小的数据,全部都要放置到堆区。


个人理解简单理解,也不太成熟,不过这是堆与栈空间管理通识,可以尝试看看相关介绍:堆和栈的区别

  1. 栈上放不了太大的东西(栈的空间是有限的)
  2. 栈上的东西没有办法直接返回给调用者(需要拷贝)

这并不是属于 Rust 的问题,当你使用 C/C++/Rust 这类语言进行开发时,你当然应该在乎你的数据是在堆上还是在栈上,否则你不可能成为成熟的工程师,尤其是嵌入式开发这种相对底层且资源受限的场景。

Box 是 Rust 使用堆的一种方式,而 C++ 是 new,C 是 malloc。使用它们的原因基本是一致的:

  1. 栈的尺寸相对小,如果数据尺寸太大,栈可能会爆掉
  2. 大尺寸数据在栈上传递拷贝可能会影响程序性能
  3. 使用 dyn 多态时,必须使用引用或智能指针,而普通引用容易遇到生命周期的问题,所以有时必须使用智能指针。

我没有写过rust,不过我觉得:

  1. 首先我觉得这个是rust的语言特性,你管他在堆还是在栈上。
  2. 另外从语义上来理解这个box,就是一个盒子,如果不要的东西扔了我就直接把这个盒子扔了,不用一件一件的扔。
  3. 且另外一方面来说,盒子里的东西可能是杂七杂八的,这种情况来说,对于程序运行期间,堆内存更大,肯定扔堆上了。
  4. 我记得程序在执行的时候,栈空间很小的,通常都是编译期间确定的变量常量以及参数等都扔在堆上,可以说栈的大小在一定程度上是确定的,本身程序在内存中是有一定布局结构的。
  5. 然后我看了你的描述,我觉得这个box也就是提供了一种防止内存溢出的方式,类似于java的内存回收啊什么的。

栈上的内存在编译时必须确定大小, 其长度是不可变的, 堆上的内存是长度可以变的.

为了满足在运行时对某个对象修改的需要, 这时候就需要放在堆上. 同时在栈中存放这个数据的引用.
引用实际上就是指针, 而指针的长度是固定的, 通常为一个字(word)的大小, 在 Rust 为一个 usize 大小, usize 在 32 位系统是 4 字节, 在 64 位系统是 8 字节. 因此满足了编译要求, 同时又支持可变长度数据的修改.

比如一个 Vec<User> 存放在线用户列表, 在运行时往里面增加或删除元素, 这时候就不可能放在栈上. Vec 本身实际就是存放在堆中, Box 一般用于结构体修改时, 结构体的数据长度发生变化的情况 例如:

#[allow(unused)]
#[derive(Debug)]
struct User {
    id: u32,
    name: String
}
fn main() {
    let mut u = User {
        id: 1,
        name: "test".to_string()
    };
    println!("user info: {:?}", u);
    u.name = "changed".to_string();
    println!("user info: {:?}", u);
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进