在 Rust 里,你几乎从不会写str,却天天在写&str。
这本身就很奇怪:如果str是字符串类型,为什么用的时候总要加个&?那个&省掉行不行?str和&str是同一个东西的两种写法,还是两个不同的类型?
这篇就把这两个类型讲清楚。
先看下面的代码
fn main() { let a = "abc"; let _: () = a; // 编译错误会显示 a 的类型 }猜猜a是什么类型?
我这里故意让编译器报错,来查看变量a的类型(这种查看变量类型的方法虽然不太方便,但是最准确)。
这里变量a的类型是&str。
当我一开始看到这个结果时是有一点懵逼的,以为会是 str 类型。
Rust 是这样描述 str 的(str - Rust):
The
strtype, also called a ‘string slice’, is the most primitive string type. It is usually seen in its borrowed form,&str. It is also the type of string literals,&'static str.
(str类型,也叫"字符串切片",是最基础的字符串类型。它通常以借用形式&str出现。它也是字符串字面量的类型,即&'static str。)
在Rust中,str被设计成Dynamically Sized Type,中文叫动态大小类型,也常叫unsized type,不定长类型,意思是这个类型的大小在编译期无法确定,只有运行时才知道。而Rust编译器在给变量分配栈空间时,必须知道大小。我使用类型注解,强制告诉编译器我要 str 类型,看看会发生什么:
fn main() { let a:str = "abc"; }编译结果:
很有意思,编译器这里报了两个错。
第一个错误提示不匹配的类型。("abc" 这里是一个表达式,表达式计算得出的结果的类型是&str,而等号左侧是str类型,导致类型不匹配;)
第二个错误提示str 类型的值的大小在编译期间未知。
注意:这是两个不同的错误!
为什么 Rust 要把 str 设计成DST呢?
其实很好理解,Rust 用 str 表示一段字符串这个类型,但是这个字符串的长度是未知的,比如 "abc", "abcd", "234%1", 这些字符串类型,但它们的长度是不同的。
而 &str 是一个指针类型,它的大小固定为16字节(64 位平台)。
fn main() { let a = "abcd"; let b = "abcde"; println!("size_of::<&str>() = {} 字节", size_of::<&str>()); }由两个字段组成
ptr:指向那段 UTF-8 字节的第一个字节
len:那段数据的字节数(不是字符数)
那 str 类型的值在哪里,怎么好像从来没见过?
字符串字面量"abc"指向的那段数据,在程序加载时被放在静态区(只读数据段),这段数据的类型是str。而字面量表达式"abc"本身的类型是&'static str——它是指向那段数据的引用。
也就是:
数据(
abc三个字节)→ 类型str,在静态区表达式
"abc"→ 类型&'static str,求值得到指向数据的引用变量
a→ 类型&str,存这个引用(16 字节,在栈上)
用一张图总结下:
另外,str 类型是Rust中的 primitive type, 也就是原生类型,而 &str 属于Rust 引用类型下的一个具体类型,并且结构由编译器内部实现。