rust中的字符串比较特别,在Rust的核心语言层面,只有一个字符串类型:字符串切片 str(或&str),而String类型是来自于Rust标准库中的类型。
Rust中的字符串是什么?
- byte的集合
- 并且它提供了一些方法,这些方法能将byte解析为文本
在Rust的核心语言层面,只有一个字符串类型:字符串切片str(或&str)
字符串切片:对存储在其他地方、utf-8编码的字符串的引用
- 字符串字面值:存储在二进制文件中,也是字符串切片
切片slice(&str)
Rust还提供了另外一种不持有所有权的类型:切片(slice)
假如我们有这样一个需求,编写一个函数:
- 它接收字符串作为参数
- 返回它在这个字符串找到的第一个单词
- 如果函数没有找到空格,返回整个字符串
如果直接实现这个需求比较繁琐,但我们可以使用字符串切片来简单的进行编写代码
字符串切片是指向字符串中一部分内容的引用:
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
println!("{},{}", hello, world); //hello,world
- 开始索引:起始位置的索引
- 结束索引:终止位置的下一个索引
关于切片的语法糖:
- 如果开始索引为0,可省略
- 如果结束索引为字符串长度,可省略
let s = String::from("hello world");
let hello = &s[..5];
let world = &s[6..];
let whole = &s[..];// &s[0..s.len()]
println!("{},{}", hello, world); //hello,world
注意:
- 字符串切片的范围索引必须发生在有效的UTF-8边界内
- 如果尝试从一个多字节的字符串中创建字符串切片,程序将会报错并推出
下面我们将实现之前提到的需求:
fn main() {
let s = String::from("yes hello world");
let first_w = first_world(&s);
println!("{}", first_w); //yes
}
fn first_world(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
字符串字面值是切片
字符串字面值被直接存储在二进制文件中
let s = "nice demo"; //s:&str
变量s的类型是&str,它是一个指向二进制程序特定位置的切片
- &str是不可变引用,所以字符串的字面值是不可变的
将字符串切片作为参数传递
有经验的开发者会采用&str作为参数类型,因为这样就可以同时接收String和&str类型的参数了:
fn first_word(s:&str)->&str{}
- 使用字符串切片,直接调用该函数
- 使用String,可以创建一个完整的String切片来调用该函数
定义函数时使用字符串切片来代替字符串引用会使我们的API更加通用,且不会损失任何功能:
fn main() {
let s = String::from("yes hello world");
let first_w = first_world(&s[..]);
let s1 = "hello demo";
let first_w1 = first_world(s1);
}
fn first_world(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
其他类型的切片
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];//slice: &[i32]
println!("{:?}", slice); //[2, 3]
}