文章目录
前言
书接上文,本篇博客将通过结合有关字符串的Rust案例来深入理解并掌握Slice
类型。旨在精准的学会切片类型的创建、作为函数的参数以及返回值以及语法糖的扩展等,同时也会分享到其他数据类型的切片,例如数组。
Rust Slice(切片)类型
切片(Slice)是对数据值的部分引用,是一种不持有所有权的数据类型。
切片这个名字往往出现在生物课上,我们做样本玻片的时候要从生物体上获取切片,以供在显微镜上观察。在 Rust 中,切片的意思大致也是这样,只不过它属于数据的取材引用。
一、编写处理字符串的函数
这里提供两种实现方法:一是通过以往的知识直接用String类型来解答,另一种是学习过本文的字符串切片类型后再去解答。
1、目标函数特点
- 接收字符串作为参数
- 返回它在这个字符串里找到的第一个单词
- 如果没有找到空格,将字符串全部返回
2、使用String粗略实现
既然是找到第一个单词,不妨找到第一个空格所在的索引,后续从头开始打印到索引位置即可:
fn main() {
println!("切片的学习");
let mut s =String::from("hello world");
let index=first_world(&s);
s.clear();//这里清空s字符串,但是仍然可以得到第一个空格的索引
println!("第一个空格出现的索引为:{}",index);
}
fn first_world(str:&String)->usize{
let bytes=str.as_bytes();
for(i,&item) in bytes.iter().enumerate(){
if item==b' '{
return i;
}
}
str.len()
}
运行结果:
3、使用字符串切片完整实现
fn main() {
println!("切片的学习");
let str=String::from("hello rust");
let new_str=first_world_slice(&str[..]);
//str.clear();不可将变量同时借用为可变和不可变的状态
println!("字符串中第一个单词是:{}",new_str);
}
fn first_world_slice(s:&str) ->&str{
let bytes=s.as_bytes();
for(i,&it) in bytes.iter().enumerate(){
if it==b' '{
return &s[..i]
}
}
&s[..]
}
运行效果:
细心观察的朋友可以看到函数的返回值被我改成了字符串切片类型,这样就可以传入字符串或者字符串切片两种类型的参数了:
- 传入的参数如果使字符串切片就直接写入
- 如果传入的是字符串类型,那么就创建一个完整的字符串切片即可
- 定义函数时使用字符串切片来代替字符串引用会使我们的
API
更加通用,且不会损失功能
二、字符串切片及其与字符串的区别
最简单、最常用的数据切片类型是字符串切片(String Slice)
例如:
fn main() {
let s = String::from("broadcast");
let part1 = &s[0..5];
let part2 = &s[5..9];
println!("{}={}+{}", s, part1, part2);
}
//运行结果:broadcast=broad+cast
- Rust 中的字符串类型实质上记录了字符在内存中的起始位置和其长度
part1
在内存中的起始位置和字符串s一致part2
在内存中的起始位置指向字符c
s
的长度为9,part1长度为5,part2长度为4
&s[x..y]
就是字符串切片类型的格式,取值上是前开后闭的: [ x , y ) [x,y) [x,y). .y
等价于0. .y
x. .
等价于位置 x 到数据结束. .
等价于位置 0 到结束
- 注意事项
- 字符串切片的范围索引必须发生在有效的
utf-8
字符边界内- 这是编码问题,后续文章会详细说明
- 如果尝试从一个多字节的字符串切片中创建字符串切片,程序会报错并退出
- 这是因为切片类型是没有所有权的
- 字符串切片的范围索引必须发生在有效的
实际上,到目前为止你一定疑惑为什么每一次使用字符串都要这样写String::from("runoob")
,直接写 "runoob"
不行吗?
事已至此我们必须分辨这两者概念的区别了。在 Rust 中有两种常用的字符串类型:str
和 String
:
- str 是 Rust 核心语言类型,就是本章一直在讲的字符串切片
(String Slice)
,常常以引用的形式出现(&str
)。
凡是用双引号包括的字符串常量整体的类型性质都是 &str :
let s = "hello";
这里的 s 就是一个 &str 类型的变量。
-
String 和 str 除了同样拥有一个字符开始位置属性和一个字符串长度属性以外还有一个容量(
capacity
)属性。 -
String 和 str 都支持切片,切片的结果是 &str 类型的数据。
注意:切片结果必须是引用类型,但开发者必须自己明示这一点
三、非字符串切片的使用
除了字符串以外,其他一些线性数据结构也支持切片操作,例如数组:
fn main() {
let arr = [1, 3, 5, 7, 9];
let part = &arr[1..3];
for i in part.iter() {
println!("{}", i);
}
}
//运行结果为:3 5
上面的解释中用到了很多Rust的基本类型知识,如果对于Rust基本类型不熟悉的朋友可以看一下本专栏里的文章。