{"content":{"title":"理解Rust中字符串的str, &str与String类型的区别","body":"# 字符串的类型大小和存储位置的概念定义及区分\r\n在学习Rust的过程中，我们经常会遇到Cannot know size until runtime: str这样的报错，而大多数的解释都是搬运Rust官方文档的解释，这是由于**str是动态大小类型(Dynamic Sized Type，DST) ，因此编译器无法确定str类型的大小**。然而给我们直观的感受是\"hello\"字面量只有5个字节，为什么在编译期间不能确定其大小呢？\r\n\r\n上述问题困扰了我很久，后来发现这是由于我对**类型大小**与**值大小**这两个概念搞混了。假设字符串字面量的类型是str，那么考虑如下代码:\r\n\r\n```\r\nlet hello: str = \"hello\";\r\nlet other: str = \"other_hello\";\r\nlet str_arr = vec![hello, other];\r\n```\r\n\r\n我们会发现hello又5个字节，而other则有11个字节，并且vec中又要求所有成员的大小是一致的，那么str到底采用几个字节？这就解释了为什么str是动态大小类型。\r\n\r\n同时还有一个比较容易混淆的点，str是动态大小类型，但是我们经常提到str字符串的长度是固定的，比较奇怪的点在于既然**str是动态大小类型，为什么其长度又是固定的呢**？这个问题其实和上一个问题一样，我们说str字符串的长度固定，指的是其具体的字符串字面量的**值**是不可变的；而动态大小类型，则指的是**类型**大小不确定。\r\n\r\n由于变量是分配在stack上，而stack上类型的大小必须固定，因此Rust中为了能够使得变量绑定字符串，引入了字符串切片类型&str。需要注意的是&str是一种类型，可以理解为str的引用类型。&str类型由两部分构成:指针和长度，这两者长度都是固定的，因此&str类型可以存储在stack上。\r\n\r\n由于字符串字面量不可变，因此Rust引入了可变字符串类型String，**其实String与&str很像，而不是str**，因为从上述分析可知**能存储在栈上的都是固定大小类型，而str类型大小不确定**。String类型由三部分构成:指针、长度和容量，相比于&str类型增加了一个容量字段，由于其所有的str长度可变，因此String存储在heap上；同时String还对其指针指向的字符串str拥有所有权，而&str类型对其指向的str没有所有权。\r\n\r\n需要注意的是`&str`**类型指向的数据并不一定分配在heap上**，它可以在如下存储区域:\r\n\r\n- 静态存储区: 字符串字面量\"hello\"是&'static str类型，这部分数据直接硬编码到程序编译的二进制文件中\r\n- Heap分配: 由String类型字符串s的切片生成s[1..]\r\n- Stack分配: 对于如下分配到栈上的字节数组，可以将其转换为&str类型的字符串:\r\n\r\n```\r\nuse std::str;\r\n\r\nlet x: &[u8] = &[b'a', b'b', b'c'];\r\nlet stack_str: &str = str::from_utf8(x).unwrap();\r\n```\r\n\r\n# 总结下来Rust中字符串的心智模型为:\r\n\r\n![str.png](https://img.learnblockchain.cn/attachments/2023/01/KXCRShof63b9b80736d72.png)"},"author":{"user":"https://learnblockchain.cn/people/720","address":null},"history":null,"timestamp":1673115884,"version":1}