1 String / &String
String
类型的变量本质是一个存放在栈上的胖指针(当然调用过程中,不用显示地按指针那样处理),共有三个字段:
- 1 pointer: 指向实际字符串值的地址,值是存放在堆上可变字节缓冲区;
- 2 length: 值Vec< u8 >长度 / 元素个数;
- 3 capacity: 当前缓冲区容量,当值的长度溢出时,寻找新的合适区间,将值重新拷贝分配到新的内存;同时更新三个字段。
&String
则表示对 String
类型数据的一个引用,也可以理解成是对上面所说的胖纸针的一个引用,示意图如下:
2 str / &str
str
表示不可变且长度未知的u8类型数据序列,这样的数据序列是可以存在于内存的多种地方的;由于其长度未知,强安全的RUST在编译时无法通过,所以str
类型数据无法独立使用,通常都采取其引用的形式,也就是字符串切片&str
,一个存放在栈上的胖指针(指针本身长度是确定的),其包含两个字段:
- 1 pointer: 指向实际字符串值的地址;
- 2 length: 数据[u8]长度 / 元素个数。
示意图如下:
3 &String 和 &str的比较
3.1 引用/指针
引用的本质就是指针,使用上略有不同,但这里为了更好地描述,我就把这两者概念模糊地来讲了。
-
【栈】
&String
->【栈】String
-> 【堆】value -
【栈】
&str
-> 【?】value (str
)
str可以在:
1)静态存储区(static storage):最常见的字面量 “foo” 类型便是&'static str
;字面量会以硬编码的方式写入程序的二进制文件中,当程序运行时,加载至ROM(Read Only Memory);
2)堆上缓冲区数据:// let s: &String = &String::from("rust"); // 隐式推断, 类型会作为&String处理 let s: &str = &String::from("rust"); // 显示标注,这里&str可以理解成是对String的全长切片
其中,
from()
里的字符串字面量"rust"也会以硬编码形式写入二进制文件中,程序运行时,加载至ROM;创建String
变量时,会在ROM中找到对应字符串,然后拷贝至堆中,返回地址、长度、容量字段。
3)栈上的数据:分配在栈上的< u8 >数组
use std::str; fn main() { let arr: [u8; 4] = [b'r', b'u', b's', b't']; // b 将char unicode转换为utf-8 let stack_str: &str = str::from_utf8(&arr).unwrap(); }
3.2 类型转换
常见的转化就不赘述了,可以参考这位博主的文章
《rust中String,&str,Vec 和&[u8]的惯用转换》https://zhuanlan.zhihu.com/p/372082802
&String
和&str
其实都可以看成是对< u8 >序列数据的引用,只不过结合上面的分析可以看出:&String
最终指向的数据只会存在于堆上,而&str
指向的数据则不然。可以完美地用&str
替换&String
(表全长String的切片),反过来就不行。
这也是字符串引用类型通常都是使用&str
的原因。
let s1:&str = &String::from("rust");
let s2:&String = &String::from("rust");
let s1:&str = "rust";
// let s2:&String = "rust"; //类型匹配错误, mismatched types expected reference `&String` found reference `&'static str`