Rust 书籍中指出:
另一个使栈快速的属性是所有在栈上的数据都必须占用已知的固定大小。
它还表示 String
存储在堆上,因为其大小不确定且可以被修改。
那么类似包含 String
的数组这样的“复合”数据结构存储在哪里? 数组的大小是固定的,但是数组的组成部分的大小可以改变。
let array: [String; 3] = ["A","B","C"];
这样的“组合”数据类型存储规则是什么?
Rust 书籍中指出:
另一个使栈快速的属性是所有在栈上的数据都必须占用已知的固定大小。
它还表示 String
存储在堆上,因为其大小不确定且可以被修改。
那么类似包含 String
的数组这样的“复合”数据结构存储在哪里? 数组的大小是固定的,但是数组的组成部分的大小可以改变。
let array: [String; 3] = ["A","B","C"];
两者都是。
一个术语的概念:讨论类型的内存布局时,不应该谈论堆栈 vs 堆,而应该谈论内联 vs 离线1:
一个简单的例子,整数是内联存储的:
// i32
+---+
| 3 |
+---+
一个典型的struct Point { x: i32, y: i32 }
也是内联存储的:
// Point
+---+---+
| x | y |
+---+---+
一个 String
,通常表示为 struct String { data: *mut u8, len: usize, cap: usize }
,它既在线上存储,也离线存储:
// String
+-------+-------+-------+
| data | len | cap |
+-------+-------+-------+
|
\
+-------------+
|Hello, World!|
+-------------+
内联部分占用了3个指针的存储空间,而离线部分是一个堆分配的记录,它包含字符串"Hello, World!"
的内容。
然而,内联并不总是意味着栈。一个Box<Point>
:
// Box<Point>
+-------+
| data |
+-------+
|
\
+---+---+
| x | y |
+---+---+
将其Point
(内联存储其数据成员)存储在堆上!
同样地,“离线”并不总是意味着“堆”:
fn main() {
let i = 3;
let r = &i;
}
在这里,r
是一个指向 i
的引用(指针),而 i
存储在栈上!
1 是的,我是凭空想象的,更好的术语将不胜感激。
所以,回到问题:
它还说
String
存储在堆上,因为其大小未知且可变。
这只是一种近似,如上所述,String
的一些数据内联存储(指针、长度和容量),而一些存储在堆上(字符串内容)。
Where are "composite" data structures such as arrays containing
String
stored? The array is fixed in size however the components of the array can change in size.
let array: [String; 3] = ["A","B","C"];
它同时存储在堆栈和堆上:
// [String; 3]
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| data | len | cap | data | len | cap | data | len | cap |
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| | |
\ \ \
+-+ +-+ +-+
|A| |B| |C|
+-+ +-+ +-+
这是 9 个指针大小的内联数据(在此处于栈上),并且在堆上进行了 3 次单独分配。
这种“复合”数据类型存储的规则是什么?
数据成员总是内联的,指针和引用可能指向离线数据,这些数据可能在堆、栈上等等...
String
只是对Vec<u8>
进行了包装,因此适用于所有Vecs
。
Vec
在堆栈上具有固定大小:它的长度、容量和指向存储实际内容的堆的指针。
因此,在堆栈上有三个Strings
数组意味着这些字符串的“元数据”位于堆栈上(长度、容量和指向数据的指针)。
然而,这些字符串的实际数据存储在堆上,因为它们的长度是可变的,正如你正确地识别出来的那样。
data
指向)。如果是这样的话,它们存储在3个单独的堆分配中,它们是否相邻取决于分配器;肯定没有保证,这就是为什么我将它们分开绘制的原因。 - Matthieu M.