Rust Slice(切片)类型

切片是「引用 + 长度」,不持有数据;`&str` 就是 `&[u8]` 的特化。

Intermediate
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
hello

Parameter 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]
MethodEffect
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.

Friends & Partners