我试图理解references和Box<T>
是如何工作的,让我们考虑一个代码示例:
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
在我的想象中,Rust在内存中保存的值为:
考虑Box<T>
的第二个代码片段:
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
x
将如何存储到Box
中?内存是什么样子的?
上面的例子来自Treating Smart Pointers Like Regular References with the Deref
Trait。对于第二个例子,书中解释为:
清单15-7和清单15-6的唯一区别是,这里我们将y
设置为指向x
中的值的box的示例,而不是指向x
的值的引用。
这是否意味着框中的y
直接指向值5
?
3条答案
按热度按时间wz8daaqr1#
简单情况下的图很好,尽管可能不太清楚,因为值和地址都使用了
5
,我在图中移动了y
以避免混淆。Box<T>
的内存是什么样的?Box
的等效图看起来类似,但添加了堆:(See下面关于此图的学究式注解)
Box
已经在堆中为我们分配了足够的空间,这里是地址0xFF01
。然后将值从堆栈移到堆中。这是否意味着框中的
y
直接指向y
持有指向Box
分配的数据的指针。它 * 必须 * 这样做,以便能够在Box
超出作用域时释放分配的内存。您正在阅读的这一章的要点是Rust将为您透明地取消引用
Box
,因此您通常不需要关心这个事实。另见:
Box
?内存有什么不同?
这可能会让你的大脑有点弯曲!
看看这两个例子的栈,这两种情况并没有真正的区别--reference和
Box
都作为指针存储在栈中,唯一的区别是 * 在代码中 *,它知道根据它是reference还是Box
来区别对待栈中的值。事实上,Rust!中的所有内容都是如此,对于计算机来说,所有内容都只是位,二进制程序中编码的结构是唯一能区分字节blob的东西。
为什么
x
在移到Box
后仍在堆栈上?细心的读者会注意到,我在堆栈中保留了
x
的值5
,这有两个相关的原因:1.这实际上是内存中发生的事情。程序通常不会"重置"它们处理完的值,因为这将是不必要的开销。Rust通过将变量标记为已移动并禁止访问已移动的变量来避免问题。
1.在这个例子中,
i32
实现了Copy
,这意味着在它被移动之后可以访问它的值,编译器实际上允许我们继续访问x
,如果x
是一个没有实现Copy
的类型,比如String
或Box
,这就不成立了。另见:
学究式图表注解
i32
占用4个字节,指针/引用占用的字节数取决于平台,但假设所有内容的大小都相同会更简单。yzckvree2#
虽然一般规则与What are the differences between Rust's
String
andstr
?的答案完全相同,但我也在这里回答。Rust引用(几乎)完全符合您的描述:一个指向内存中某个值的指针。(并不总是这样。例如,切片也包含一个长度,指向traits的指针也包含一个v表。这些被称为胖指针。)在开始时,
Box<T>
是一个值,就像Rust中的任何其他值一样,因此,区别是显而易见的-一个是对内存中某个位置的引用,而第二个是内存中某个位置的值。令人困惑的是,Box<T>
内部包含了一个对内存的引用,但该引用是在堆而不是堆栈上分配的,两者之间的区别在于堆栈是函数本地的,并且非常小(在我的macOS上,最大值为8192 KiB)。例如,您不能执行类似这样的操作,原因如下:
最重要的原因是
foo()
返回后a
将不在那里。内存将被清除(虽然不总是如此),并且可能很快会被更改为另一个值。这在C和C++中是未定义的行为,在Rust中是一个错误,它不允许任何未定义的行为(在不使用unsafe
的代码中)。另一方面,如果您这样做:
一些与我们相关的事情将会发生:
a
的所有权将转移到调用方为方便起见,
Box<T>
在很多情况下会像引用一样,因为这两个函数可以互换使用。例如,请看下面的C程序,其中我们提供了与第二个示例类似的功能:正如你所看到的,指针被用来存储内存的地址,并被进一步传递。
pdkcd3nj3#
“5”被复制到堆中,“y”智能指针指向它。由于指针的大小是固定的,它们被存储在堆栈中。看起来像这样:
如果你需要得到对智能指针“y”的引用
你正在得到指针的引用,你正在得到存储在堆中的值的引用。这可能是因为智能指针实现
Deref
。这是Deref
如何为智能指针实现的返回值为
&self.value
。如果它没有返回引用,则当该值被使用一次时,智能指针将失去所有权。通过返回引用,rust确保指针始终是所有者