1. str 和 &str
看下面的代码:
let s = "hello";
其中,"hello" 的数据类型是 str,变量 s 的数据类型是 &str。有点难理解,我们可以与 c 语言比较,然后就容易理解了。上面的代码在 c 语言中是下面的样子:
char *s = "hello";
很有意思吧? 在 c 语言中, s 是指针。但是,变量 s 用起来很不安全,因为不知道 s 指向的数据占用了多少内存空间。
于是在 rust 中,s 是切片。切片是一个结构体,包含两个字段,一个是指向数据的指针,另一个是数据的长度。因此,我们有可能采用必要的措施,安全的使用变量 s。
2. String
切片 &str 虽然可以安全使用,但是,我们很难动态修改其内容 —— 其地址、长度都是固定的。于是 rust 提供了数据类型 String。
String 包含了数据指针、数组容量、数据长度等三个字段。如果新修改的数据长度在其容量范围内,数据可以原地修改。如果新修改的数据长度超出了容量范围,它可以重新申请更大的内存。
于是我们看到,String 和 &str 是两个完全不一样的结构体。为什么字符串要保留这两种形式?原因就是效率。rust 希望在数组容量不会变化的时候,用 &str。在数组长度可能发生变化的情况下,使用 String。
3. &str 转 String
可以用 &str 的 to_string() 方法,或者用 String::from() 方法。例如:
let s: String = "hello".to_string();
let t: String = String::from("hello");
4. String 转 &str
这个很有意思,在 rust 中,凡是需要用 &str 的地方,都可以直接用 &String 类型的数据。例如:
fn greet(s: &str) {...
}fn main() {let s: String = String::from("hello");greet(&s);
}
难道 rust 故意为 String 设计了这么一个机关? 如果这样的话,rust 内部岂不打破了某种简单化的设计原则?事实上,上述转换是借助于 deref coercing 这个特性实现的。如果我们自定义的数据类型也想实现类似的自动转换,实现这个特性即可。
5. 何时使用 $str,何时使用 String?
规则很简单,一般情况下,&str 用于只读数据,String 用于可修改的数据。至于特殊情况,你自己应该会选择的。