在Rust中提到字符串,通常指的是rust核心语言中的字符串类型str(字符串slice)和rust标准库中的String类型。
除了str和String这两个常用的字符串类型,在标准库中还有一些其他的字符串类型,例如OsString、OsStr、CString、CStr。
本文将介绍str和String两个字符串类型。
str和String是Rust中两种不同的核心字符串类型。尽管它们在技术上是不同的类型,但在大多数情况下,它们是同一种东西。它们都表示存储在连续内存区域中的任意长度的UTF-8字符序列。str和String之间唯一的实际区别在于内存的管理方式。
大多数情况下,将使用String或&str,但永远不会使用str。
str字符串slice
str是rust核心语言中内置的字符串类型,它通常以"借用"&str的形式出现,str即字符串slice,是一些存储在别处的UTF-8编码的字符串数据的引用。
例如编写的rust代码中的字符串字面值被存储在程序的二进制中。
在学习str之前,先看一下slice类型。slice是rust中没有所有权的数据类型之一,使用slice可以引用集合中一段连续的元素序列,而不用引用整个集合。
字符串slice是String中一部分值的引用,例如:
fn main() {
let s = String::from("hello world");
let hello = &s[..5];
let mid = &s[4..7];
let world = &s[6..];
println!("{}", hello); // "hello"
println!("{}", mid); // "o w"
println!("{}", world); // "world"
}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)。例如:
fn main() {
let s = String::from("你好 世界");
let hello = &s[..1]; // thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside '你' (bytes 0..3) of `你好 世界`'
println!("{}", hello);
}前面声明字符串slice时是使用的类型字段推断,还需要知道字符串slice的类型声明是&str,例如:
fn main() {
let s = String::from("hello world");
let hello :&str = &s[..5];
println!("{}", hello);
}字符串字面值实际上就是字符串slice:
let hello :&str = "hello";上面代码中的hello变量的类型是&str,它是一个指向二进制程序特定位置的字符串slice,&str是一个不可变引用,字符串字面值是不可变的。
String是一种集合类型
Rust中的字符串String是以字节集合加上一些方法实现的。
可以认为String是一种集合类型,支持集合类型常见增删改查操作。
String类型由Rust的标准库提供,是可变的、有所有权的、以UTF-8编码字符串数据的引用。
使用String类型的new函数,可以创建一个空的字符串,调用push_str方法可以向其中添加字符串slice:
fn main() {
let mut s = String::new();
s.push_str("hello");
s.push_str(" ");
s.push_str("world");
println!("{}", s);
}rust中的String类型字符串不支持索引访问,例如s[0]会出现编译错误。
如果去查看String类型源码的话,会发现它实际上是一个Vec<u8>的封装:
pub struct String {
vec: Vec<u8>,
}
....可以看出String类型是字节vector加上一些方法实现的。
遍历字符串
rust字符串UTF-8编码的特性,决定了有两种遍历字符串的方式,以字符形式和以字节形式。
fn main() {
let s = "你好";
for c in s.chars() {
print!("{}, ", c) // 你, 好,
}
println!();
for s in s.bytes() {
print!("{}, ", s) // 228, 189, 160, 229, 165, 189,
}
}