讲解
切片(slice)是对一段连续序列的「视图」:它不拥有数据,只是指向序列的一部分(起点 + 长度)。数组切片的类型写作 &[T],字符串切片的类型是 &str。切片是引用家族的一员,天然遵守借用规则——拿到切片不需要所有权转移。
字符串切片 &str 是你每天都会打交道的类型,因为所有字符串字面量都是 &str:"你好" 存的是指向程序二进制内嵌数据的切片。由此引出一条重要的 API 设计建议:函数需要读字符串时,参数类型写 &str 而不是 &String。&String 可以自动转换成 &str(这叫 deref coercion),而反过来不行——写 &str 参数的函数既能收 String 的引用,也能收字面量和切片,适用面最大。
数组和 Vec 同样能切:&a[1..3] 是第 2、3 个元素的切片。切片越界和数组下标一样会 panic,安全版本是 .get(1..3) 返回 Option。对 UTF-8 字符串按字节位置切片时要格外小心:切在汉字中间会 panic(中文字符占 3 字节),处理多语言文本请用 .chars() 按字符遍历。
示例
经典练习:取字符串的第一个单词(返回 &str 切片):
fn first_word(s: &str) -> &str {
for (i, c) in s.char_indices() {
if c == ' ' {
return &s[..i];
}
}
&s[..] // 没有空格:整个字符串就是第一个单词
}
fn main() {
let sentence = String::from("你好 世界 rust");
let word = first_word(&sentence); // &String 自动转成 &str
assert_eq!(word, "你好");
let literal = "rust 语言";
assert_eq!(first_word(literal), "rust"); // 字面量本身就是 &str
let single = "只有一个词";
assert_eq!(first_word(single), "只有一个词");
println!("first_word 三种输入全部正确");
}
数组切片与安全切片:
fn main() {
let a = [10, 20, 30, 40, 50];
let mid: &[i32] = &a[1..4]; // [20, 30, 40]
assert_eq!(mid.len(), 3);
assert_eq!(mid[0], 20);
// .get(range) 越界给 None 而不是 panic
assert!(a.get(1..100).is_none());
assert_eq!(a.get(0..2), Some(&[10, 20][..]));
let sum: i32 = mid.iter().sum();
println!("中段元素和:{sum}");
assert_eq!(sum, 90);
}
常见坑
- 按字节切中文字符串:"你好"[0..1] 直接 panic(切在字符边界之外)。遍历用 .chars(),切片先确认边界(.is_char_boundary(i))。
- 以为切片拥有数据:切片只是视图,原数据被释放或可变借用冲突时切片就失效——编译器会拦,但读报错时要理解这层关系。
- 参数写 &String:&String 参数把字面量和切片挡在门外;写 &str,调用方传 &my_string 时编译器自动转换,两全。
- 切片越界:&a[..] 的范围越界和下标越界一样 panic;来源不可信时用 .get(range)。
小结
切片 = 不拥有数据的连续序列视图;&str 是字符串切片,字面量都是它;函数读字符串参数写 &str;多语言文本别按字节乱切。下一章开始自定义类型:结构体。