Rust Slice(切片)类型
切片是「引用 + 长度」,不持有数据;`&str` 就是 `&[u8]` 的特化。
Key Points`&v[a..b]` 借用一段连续内存,不复制,也不能超出原数据寿命。
基本用法
Rust
fn main() {
let v = vec![10, 20, 30, 40, 50];
let s = &v[1..4]; // [20, 30, 40]
println!("{:?}", s);
println!("{}", s.len());
let head = &v[..2]; // [10, 20]
let tail = &v[3..]; // [40, 50]
let all = &v[..];
println!("{:?} {:?} {:?}", head, tail, all);
}Output
[20, 30, 40]
3
[10, 20] [40, 50] [10, 20, 30, 40, 50]A slice is a fat pointer: a pointer to the first element plus a length. So &v[1..4] and &v[0..3] are both "pointer + 3" with no extra cost.
切片作为参数
Rust
fn sum(nums: &[i32]) -> i32 {
nums.iter().sum()
}
fn first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
fn main() {
let v = vec![1, 2, 3];
let arr = [4, 5, 6];
println!("{} {}", sum(&v), sum(&arr)); // Vec 和数组都能传
let text = "hello world";
println!("{}", first_word(text));
}Output
6 15
helloParameter type &[i32] rather than &Vec<i32> lets callers pass a Vec, an array or a slice. Deref coercion does the conversion.
&str 也是切片
Rust
fn main() {
let s = "hello world";
let hello = &s[0..5];
let world = &s[6..11];
println!("{} {}", hello, world);
// 中文字符按字节切会 panic
let cn = "中文";
// let bad = &cn[0..1]; // panic: byte index 1 is not a char boundary
let ok = &cn[0..3]; // 第一个汉字
println!("{}", ok);
}Output
hello world
中字符串切片按字节索引,不是字符。
&"中文"[0..1] 会在运行时 panic(byte index 1 is not a char boundary)。按字符处理请用 chars() 或 char_indices()。切片的常见操作
Rust
fn main() {
let v = vec![3, 1, 4, 1, 5];
let mut s = v.as_slice(); // Vec → &[T]
s = &s[1..];
println!("{:?}", s);
// 排序需要 &mut [T]
let mut v2 = vec![3, 1, 4];
v2.sort();
println!("{:?}", v2);
// 不可变切片也能用二分查找(要求已排序)
println!("{:?}", v2.binary_search(&3));
// 分块
for chunk in v2.chunks(2) {
println!("{:?}", chunk);
}
}Output
[1, 4, 1, 5]
[1, 3, 4]
Ok(1)
[1, 3]
[4]| Method | Effect |
|---|---|
len / is_empty | 长度 / 是否为空 |
chunks(n) | 按 n 个一块切 |
windows(n) | 滑动窗口 |
split_at(i) | 切成两半 |
iter / iter_mut | 迭代 |
contains | 是否包含 |
sort / sort_by | 排序(需 &mut) |
切片与原数据的关系
Rust
fn main() {
let mut v = vec![1, 2, 3];
let s = &v[0..2];
// v.push(4); // 编译错误:不能在不可变借用时可变借用
println!("{:?}", s);
// 切片用完之后可以改
drop(s); // 其实不需要,编译器按最后使用点自动算
v.push(4);
println!("{:?}", v);
}Output
[1, 2]
[1, 2, 3, 4]A borrow ends at its last use (non-lexical lifetimes). So after the println! you can mutate v again without waiting for s to go out of scope.
基本用法
Rust
fn main() {
let v = vec![10, 20, 30, 40, 50];
let s = &v[1..4]; // [20, 30, 40]
println!("{:?}", s);
println!("{}", s.len());
let head = &v[..2]; // [10, 20]
let tail = &v[3..]; // [40, 50]
let all = &v[..];
println!("{:?} {:?} {:?}", head, tail, all);
}Output
[20, 30, 40]
3
[10, 20] [40, 50] [10, 20, 30, 40, 50]切片是胖指针:一个指向首元素的指针加一个长度。所以 &v[1..4] 和 &v[0..3] 在内存里都是「指针 + 3」,没有额外开销。
切片作为参数
Rust
fn sum(nums: &[i32]) -> i32 {
nums.iter().sum()
}
fn first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
fn main() {
let v = vec![1, 2, 3];
let arr = [4, 5, 6];
println!("{} {}", sum(&v), sum(&arr)); // Vec 和数组都能传
let text = "hello world";
println!("{}", first_word(text));
}Output
6 15
hello参数写 &[i32] 而不是 &Vec<i32>,调用方就能传 Vec、数组、切片。这是 Rust 的「解引用强制转换」(deref coercion)在起作用。
&str 也是切片
Rust
fn main() {
let s = "hello world";
let hello = &s[0..5];
let world = &s[6..11];
println!("{} {}", hello, world);
// 中文字符按字节切会 panic
let cn = "中文";
// let bad = &cn[0..1]; // panic: byte index 1 is not a char boundary
let ok = &cn[0..3]; // 第一个汉字
println!("{}", ok);
}Output
hello world
中String slicing indexes bytes, not characters.
&"中文"[0..1] panics at runtime (byte index 1 is not a char boundary). Use chars() or char_indices() for characters.切片的常见操作
Rust
fn main() {
let v = vec![3, 1, 4, 1, 5];
let mut s = v.as_slice(); // Vec → &[T]
s = &s[1..];
println!("{:?}", s);
// 排序需要 &mut [T]
let mut v2 = vec![3, 1, 4];
v2.sort();
println!("{:?}", v2);
// 不可变切片也能用二分查找(要求已排序)
println!("{:?}", v2.binary_search(&3));
// 分块
for chunk in v2.chunks(2) {
println!("{:?}", chunk);
}
}Output
[1, 4, 1, 5]
[1, 3, 4]
Ok(1)
[1, 3]
[4]| 方法 | 作用 |
|---|---|
len / is_empty | 长度 / 是否为空 |
chunks(n) | 按 n 个一块切 |
windows(n) | 滑动窗口 |
split_at(i) | 切成两半 |
iter / iter_mut | 迭代 |
contains | 是否包含 |
sort / sort_by | 排序(需 &mut) |
切片与原数据的关系
Rust
fn main() {
let mut v = vec![1, 2, 3];
let s = &v[0..2];
// v.push(4); // 编译错误:不能在不可变借用时可变借用
println!("{:?}", s);
// 切片用完之后可以改
drop(s); // 其实不需要,编译器按最后使用点自动算
v.push(4);
println!("{:?}", v);
}Output
[1, 2]
[1, 2, 3, 4]借用的生命周期在最后一次使用处结束(非词法生命周期,NLL)。所以 println! 之后 v 就能再改了,不必等 s 离开作用域。