本章主要介绍 rust 的字符串相关类型。
fn main() {let my_string: String = String::from("This is a new string");println!("{}", my_string); 输出 This is a new string}
在实践中,使用 format! 来创建更实用,它的语法和 println! 类似:
fn main() {let a = 1;let b = 2;let my_string = format!("{} + {} = {}", a, b, a + b);println!("{}", my_string); 输出 1 + 2 = 3}
字符串的内存表示
rust 的字符串在内存中是 UTF-8 编码的字节序列,使用 len 方法可以获得占用的字节数:
let my_string = format!("A");println!("{}", my_string.len()); 输出 1let my_string = format!("字");println!("{}", my_string.len()); // 输出 3 ,因为 UTF-8 的汉字占 3 字节
字符串不能直接用下标取出其中的第 n 个字符,取字符需要这样:
let my_string = format!("我的字符串");println!("{}", my_string.chars().nth(3).unwrap()); // 输出 符
注意:这是个高开销的操作!因为在 UTF-8 编码的字节序列中找出来第 n 个字符需要从字符串开头进行遍历,是个缓慢的过程。
clone(复制)
大多数类型的变量可以用 clone 方法来复制,字符串也不例外。
fn main() {let my_int = 123;let another_int = my_int.clone();println!("{}", another_int); // 输出 123let my_string = format!("ABC");let another_string = my_string.clone();println!("{}", another_string); // 输出 ABC}
注意:复制长字符串是个高开销的操作!它需要另外创建一个等长的字节序列将字符串中的每个字节逐一复制进去。一般来说,需要尽量避免字符串复制。复制长数组也是一样的道理。
Borrowship 的概念
在 rust 中,避免复制的常见方法是 Borrowship :可以将任意类型的值暂时“借”出去,类似于 C++ 的引用,不过也有一些区别。
类似于变量的可变性,引用也分为不可变和可变的。不可变引用不能改变其值,但是对于同一个值可以同时存在多个不可变引用:
fn main() {let my_string = format!("ABC");{let my_string_ref: &String = &my_string; // 不可变引用let my_string_ref_2 = &my_string; // 另一个不可变引用println!("{} {}", my_string_ref, my_string_ref_2); // 输出 ABC ABC}}
对于可变引用,可以改变值:
fn main() {let mut my_string = format!("ABC");{let my_string_ref_mut: &mut String = &mut my_string; // 可变引用*my_string_ref_mut = format!("DEF");}println!("{}", my_string); // 输出 DEF}
对于一个值,同一时刻只能有一个可变引用,否则会编译失败:
let mut my_string = format!("ABC");{let my_string_ref_mut = &mut my_string;let my_string_ref_mut_2 = &mut my_string; // 编译错误*my_string_ref_mut = format!("DEF");}
&str
在实践中,引用整个字符串的情况是比较少的,更常见的是引用一个字符串的其中一个片段。例如,使用 trim 方法可以获得字符串除去两头的空白符之后的中间部分,此时需要用到 &str 类型:
fn main() {let my_string = format!(" ABC ");let trimmed_str: &str = my_string.trim();println!("{}", trimmed_str); // 输出无空格的 ABC}
&str 的含义是“对字符串中一部分的不可变引用”。当然,它也可以引用字符串的全部,使用 String 的 as_str 方法可以获得引用整个 String 内容的 &str 。
fn main() {let my_string = format!("DEF");let my_str = my_string.as_str();println!("{}", my_str); // 输出 DEF}
所以实践中 &String 类型是不实际使用的,而是以 &str 代替。使用 &str 的好处是可以尽量避免任何字符串片段的复制,而是以引用的方式来快速访问。
&str 字面量
在 rust 中直接写双引号包裹的字符串时,获得的就是一个 &str (而非 String ):
let my_str: &str = "ABC\n";
类似于其他语言,其中可以包含 \n 之类的转义符。但有时转义符太多会影响代码阅读,因而另一种形式的写法也很常用:
let my_str: &str = r#"This is a line.This is another line."#;
在 r#"..."# 中可以包含换行和各种特殊符号。
String 和 &str 还有很多方法,可以参考文档:
https://doc.rust-lang.org/std/string/struct.String.html
https://doc.rust-lang.org/std/primitive.str.html
rust 的 Borrowship 是一个独特而复杂的体系,在后面的文章中将会专门详细介绍。




