切片
上一节中提到了所有权概念,所有权只能归属于一个变量,而使用引用可以在不获取或有权的情况下方便地获取变量的值。除了引用,Rust 还有另外一种不持有所有权的数据类型:切片(slice)。切片允许我们引用集合中某一段连续的元素序列,而不是整个集合。
字符串切片
字符串切片是指向 String 对象中某个连续部分的引用,创建一个字符串:
let s = String::from("hello world");
复制代码
创建一个字符串切片,从索引 0 开始到索引 5 结束,但不包括 5:
let s1 = &s[0..5]; // hello
// 更简单的写法,与上面等价
let s2 = &s[..5] // hello
复制代码
创建从索引 6 开始到索引 11 的切片:
let s3 = &s[6..11]; // world
// 更简单的写法,与上面等价
let s4 = &s[6..] // world
复制代码
创建完整的切片:
let s5 = &str[..]; // hello world
复制代码
可以尝试编写一个函数,利用切片获取一句英文的首单词:
fn first_word(s: &String) -> &str /* 字符串的切片类型 */ {
// 将字符串转为字节,例如单个字符a会转为97
let bytes = s.as_bytes();
// 我们通过句子中的第一个空格来判断首单词
// 这里创建一个空格的字面量字节,等同于32
let blank = b' ';
// 通过iter()创建迭代器,再使用enumerate()将迭代成员附带索引
// 每次迭代得到一个元组,包含索引和对应成员的引用
for (i, &item) in bytes.iter().enumerate() {
// 如果当前这个字节是空格的话
// 那么这个空格之前的字母就是首单词
if item == blank {
// 利用切片截取首单词并返回
return &s[0..i];
}
}
// 没有找到空格,说明传入参数本身就是一个单词
// 可以直接返回完整的切片
return &s[..];
}
复制代码
下面来使用这个函数:
let s = String::from("hello world");
let word = first_word(&s);
println!("{}", word);
// hello
复制代码
当字符串是一个完整的单词时:
let s = String::from("hello");
let word = first_word(&s);
println!("{}", word);
// hello
复制代码
字符串字面量就是切片
其实在我们使用字面量的方式定义字符串时,创建的就是一个字符串切片 &str,由于 &str 是一个不可变的引用,所以字符串字面量自然是不可变的:
let s = "hello world";
// 显式标注类型
let s: &str = "hello world";
复制代码
将字符串切片作为参数
注意一下上边的 first_word 的参数要求是 &String,所以当我们使用切片 &str 作为参数时,将会报错:
let s = "hello world";
first_word(&s) // error,预期&String,却得到了&str
复制代码
为了函数更好的兼容性,可以使用切片类型来兼容两种字符串:
// 将参数改为切片类型&str
fn first_word(s: &str) -> &str {
/* 省略 */
}
first_word("hello world"); // hello
first_word(&String::from("hello world")); // hello
复制代码
其他类型切片
除了字符串以外,数组也支持数组切片,下面创建一个数组:
从开始到索引 2 的切片:
let a1 = &a[..2];
// [1, 2]
复制代码
从索引 2 到结束的切片:
let a2 = &a[2..];
// [3, 4, 5]
复制代码
从索引 1 到索引 3 的切片:
let a3 = &a[1..3];
// [2, 3]
复制代码
切片的存储
切片数据结构在内部存储了指向起始位置的引用和一个描述切片长度的字段,这个描述切片长度的字段等价于结束索引减去开始索引:
let s = String::from("hello world");
let world = &s[6..] // world
复制代码
下图展示了字符串 s 和切片 world 的在内存中的存储方式,s 指向了索引 0 的字符 h,长度是 11,容量是 11;world 指向了索引 6 的字符 w,长度是 5,由于 world 只是 s 的一段切片引用,所有不存在容量属性:
评论