(六)Rust切片类型

本文最后更新于 2024年6月28日 中午

切片(Slices)是引用的一种,用来引用集合中连续的一串元素。

假想一个场景,一个字符串处理函数,函数需要将字符串按照空格分隔,同时返回字符串中第一个单词。

在没有切片的情况下,应该如何实现?下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// &String类型参数s作为输入,返回字符串中第一个单词结束的索引,索引类型为usize
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes(); // 为了遍历字符串,将字符串转换为数组

for (i, &item) in bytes.iter().enumerate() {// iter() 返回每一个值的集合
// enumerate() 将集合包装为(索引,值)元组形式
if item == b' ' {
return i; // 当前元素为空格时,返回当前索引
}
}

s.len() // 字符串不存在空格,返回字符串长度
}

fn main() {
let mut s = String::from("hello world"); // 声明一个可变字符串s

let word = first_word(&s); // 将s的引用传入first_word函数,获取索引

s.clear(); // 清除字符串,s等于""

// word变量仍然有效,但是s已经发生变化,word值已经没有意义了
}

上述代码可以实现相应功能,但是需要人工同步索引值和字符串,这耗费精力也容易出错。使用Rust字符串切片可以解决同步索引值和字符串问题。

字符串切片

字符串切片是String的部分引用,它的声明像下面这样:

1
2
3
4
let s = String::from("hello world");

let hello = &s[0..5];
let world = &s[6..11];

hellos字符串的一部分,范围由[0..5]明确给出。范围的形式可以理解为[starting_index..ending_index]starting_index是切片的第一个元素位置,ending_index比切片中最后一个元素位置大1,可以理解为左闭右开。下面图片是部分引用和字符串的区别:

还有一些范围的简写方式,请看代码:

1
2
3
4
5
6
7
8
9
let s = String::from("hello");
// 表示字符串
let slice1 = &s[0..2]; // he
let slice2 = &s[..2]; // he

let len = s.len();

let slice3 = &s[0..len]; // hello
let slice4 = &s[..]; // hello

使用切片对字符串分割函数重写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// &String类型参数s作为输入,返回字符串切片,字符串切片类型为&str
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();

for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i]; // 返回字符串切片
}
}

&s[..] // 返回字符串切片
}

fn main() {
let mut s = String::from("hello world"); // 创建字符串s

let word = first_word(&s); // 调用函数获取首单词切片
//CASE ERROR================================================================
s.clear(); // error! // 1. 清除字符串,令s等于""

println!("the first word is: {word}"); // 2. 打印字符串内容
//CASE NOERROR==============================================================
println!("the first word is: {word}"); // 2. 打印字符串内容

s.clear(); // 1. 清除字符串,令s等于""
//CASE END==================================================================
}

在打印word之前,若对字符串修改,编译将无法通过。使用字符串切片,编译器能够帮助完成文章开头提及的同步问题,只要编译能通过,那么字符串引用一定有效。

其他类型切片

字符串切片针对的是字符串类型,当然还有更通用的切片类型,考虑这个数组:

1
2
3
4
5
let a = [1,2,3,4,5];

let slice = &a[1..3];

assert_eq!(slice,&[2,3]);

这个切片类型为&[i32],它的工作原理和字符串切片一样,保存第一个元素的引用和长度。其余所有类型的切片都与这个切片类似。

参考

Rust docs


(六)Rust切片类型
https://www.happyallday.cn/Rust/RustLang_06_TheSliceType/
作者
DevoutPrayer
发布于
2024年6月28日
更新于
2024年6月28日
许可协议