切片
Rust 中的 slice(切片)是一种引用集合中连续元素的视图,而不拥有这些元素。它类似于数组或向量的子视图,使用 &[T] 表示不可变切片,&mut [T] 表示可变切片。Slice 是借用的一部分,遵守借用规则,确保内存安全。Slice 常用于字符串、数组和向量,帮助避免不必要的拷贝,提高效率。
1. Slice 简介
- 什么是 slice? :Slice 是对数据序列的引用视图,指向连续内存块。不拥有数据,只借用。长度在运行时确定。
- 语法 :
&[T](不可变)、&mut [T](可变)。T 是元素类型。 - 优势 :零拷贝访问子集;函数参数通用(如接受
&[i32]而非 Vec 或[i32; N])。 - 与数组/向量的关系 :数组是固定大小,向量是动态。Slice 可以从两者创建。
- 字符串 slice :
&str是&[u8]的特殊形式,处理 UTF-8。
fn main() {
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
println!("{}", hello); // 输出: hello
println!("{}", world); // 输出: world
}
- 解释 :
[start..end]是半开区间(包括 start,不包括 end)。&arr[..]是全切片。Slice 借用 arr,借用规则适用。
对于字符串而言,切片就是对 String 类型中某一部分的引用,它看起来像这样:
胖指针(Fat Pointer)
- 数据结构:对比普通引用(1个字长指针)与切片引用(2个字长)。
- 组成部分:
- Pointer:指向数据的起始位置。
- Length:切片包含的元素个数。
- 内存视图:在栈上存储元数据,在堆/静态区查看数据。
2. 创建 Slice
Slice 通过借用和范围运算符创建。
- 范围语法 :
[start..end]:从 start 到 end-1。[..end]:从 0 到 end-1。[start..]:从 start 到结束。[..]:整个集合。
- 从向量/数组 :直接 &vec[start..end]。
- 边界检查 :运行时检查,如果越界 panic!(安全)。
示例:各种创建方式
fn main() {
let vec = vec![10, 20, 30, 40, 50];
let full = &vec[..]; // 全切片: [10, 20, 30, 40, 50]
let first_three = &vec[0..3]; // [10, 20, 30]
let last_two = &vec[3..]; // [40, 50]
println!("{:?}", first_three);
}
- 解释 :Vec 和数组都支持。Slice 的 len() 返回元素数,get(i) 返回 Option<&T>(安全访问)。
可变 slice
fn main() {
let mut vec = vec![1, 2, 3];
let slice = &mut vec[1..3]; // 可变借用
slice[0] = 20; // 修改 vec[1]
println!("{:?}", vec); // 输出: [1, 20, 3]
}
- 解释 :可变 slice 允许修改元素,但遵守独占借用规则。
3. 字符串 Slice (&str)
字符串 slice 是常见的,处理 String 或 str。
示例:字符串 slice
fn first_word(s: &str) -> &str { // 既能接受 String 的切片,也能接受字符串字面量
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
fn main() {
let s = String::from("hello world");
let word = first_word(&s); // &String 隐式转为 &str
println!("{}", word); // 输出: hello
// s.clear(); // 错误!word 借用期间不能修改 s
}
- 解释 :
&str是 UTF-8 安全的。as_bytes() 转为&[u8]。切片索引必须在字符边界(否则 panic!)。用 chars() 或 bytes() 迭代以避免。
4. 多维 Slice
Slice 可以是多维的,如 &[[T]]。
示例:矩阵 slice
fn main() {
let matrix = vec![vec![1, 2], vec![3, 4]];
let row = &matrix[0][..]; // &[i32]: [1, 2]
println!("{:?}", row);
}
- 解释:嵌套借用。复杂时考虑扁平化或专用 crate。
注意事项: 索引越界:如果你请求的范围超出了集合边界(如
&s[0..100]),Rust 会在运行时 panic。 UTF-8 字符:对于字符串切片,索引必须落在字符边界上。如果在多字节字符(如中文)中间切片,程序会崩溃。