在Rust中提到字符串,通常指的是rust核心语言中的字符串类型str(字符串slice)和rust标准库中的String类型。 除了strString这两个常用的字符串类型,在标准库中还有一些其他的字符串类型,例如OsStringOsStrCStringCStr。 本文将介绍strString两个字符串类型。

str字符串slice

str是rust核心语言中内置的字符串类型,它通常以"借用"&str的形式出现,str即字符串slice,是一些存储在别处的UTF-8编码的字符串数据的引用。 例如编写的rust代码中的字符串字面值被存储在程序的二进制中。

在学习str之前,先看一下slice类型。slice是rust中没有所有权的数据类型之一,使用slice可以引用集合中一段连续的元素序列,而不用引用整个集合。

字符串slice是String中一部分值的引用,例如:

1fn main() {
2   let s = String::from("hello world");
3   let hello = &s[..5];
4   let mid = &s[4..7];
5   let world = &s[6..];
6   println!("{}", hello); // "hello"
7   println!("{}", mid);   // "o w"
8   println!("{}", world); // "world"
9}

rust中slice的语法是使用[start_index..end_index]指定的范围创建一个slice,包含start_index处的元素,而不包含end_index处的元素,rust中切片slice划得的元素个数是end_index - start_index(在这点上与go语言中的slice十分相似)。

需要注意字符串slice指定范围边界的索引必须是有效的UTF-8字符串边界,如果从一个多字节字符的中间位置创建字符串slice,程序将会panic(在这点上与go语言对string类型进行切片操作是不同的,在go中不会panic)。例如:

1fn main() {
2   let s = String::from("你好 世界");
3   let hello = &s[..1]; // thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside '你' (bytes 0..3) of `你好 世界`'
4   println!("{}", hello); 
5}

前面声明字符串slice时是使用的类型字段推断,还需要知道字符串slice的类型声明是&str,例如:

1fn main() {
2   let s = String::from("hello world");
3   let hello :&str = &s[..5];
4   println!("{}", hello); 
5}

字符串字面值实际上就是字符串slice:

1let hello :&str = "hello";

上面代码中的hello变量的类型是&str,它是一个指向二进制程序特定位置的字符串slice,&str是一个不可变引用,字符串字面值是不可变的。

String是一种集合类型

Rust中的字符串String是以字节集合加上一些方法实现的。 可以认为String是一种集合类型,支持集合类型常见增删改查操作。 String类型由Rust的标准库提供,是可变的、有所有权的、以UTF-8编码字符串数据的引用。

使用String类型的new函数,可以创建一个空的字符串,调用push_str方法可以向其中添加字符串slice:

1fn main() {
2    let mut s = String::new();
3    s.push_str("hello");
4    s.push_str(" ");
5    s.push_str("world");
6    println!("{}", s);
7}

rust中的String类型字符串不支持索引访问,例如s[0]会出现编译错误。

如果去查看String类型源码的话,会发现它实际上是一个Vec<u8>的封装:

1pub struct String {
2    vec: Vec<u8>,
3}
4....

可以看出String类型是字节vector加上一些方法实现的。

遍历字符串

rust字符串UTF-8编码的特性,决定了有两种遍历字符串的方式,以字符形式和以字节形式。

 1fn main() {
 2    let s = "你好";
 3    for c in s.chars() {
 4        print!("{}, ", c) // 你, 好, 
 5    }
 6    println!();
 7    for s in s.bytes() {
 8        print!("{}, ", s) // 228, 189, 160, 229, 165, 189,
 9    }
10}