Bootstrap

【Rust中的切片Slice】


前言

切片对于使用过Go的开发者并不陌生,其定义是一块内存视图,用指针和长度来表示。Rust中切片是引用类型,所以它并不具备所有权。


通过例子理解Rust切片

当有这样的一种需求,开发者需要根据间隔符提取字符串中的第一个单词,如果没有间隔符则返回整个字符串。
在不使用切片的情况下,我们可以这样做:

fn slicetest(s: &String) -> usize {
    let sbyte = s.as_bytes();
    for (i, &item) in sbyte.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }
    s.len()
}
fn main() {
    let mut s = "asdfas  fasfdasf".to_string();
    let tail = slicetest(&s);
    s.clear();
    println!("{}", tail);
}

如上,开发者在不使用slice的情况下,可以通过遍历字符串将其中符合条件的index返回,那么这里会有什么问题?
如果在clear后我们继续使用tail,但是tail已经没有了意义,因为字符串已经清空了,找不到第一个单词,且编译期没有帮开发者发现问题

Rust中的切片

Rust中的切片是指向了一块内存,包含内存首地址以及数据长度的指针,所以它是一般的指针的两倍大小,并且切片是一种引用类型,它并不拥有所有权。
所有权一章中,我们简单描述过了rust的所有权机制,这里再简单表述一遍,引用不拥有所有权,其生命周期跟随拥有所有权的变量主人,同时引用区分可变引用以及不可变引用,可变引用与不可变引用不能同时存在,同时只能有一个可变引用。

那么按照上述问题描述,用切片如何解决这种取一部分容器内数据的问题呢?

fn slicetest(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 mut s = "asdfas  fasfdasf".to_string();
    let firstword = slicetest(&s);
    s.clear();
    println!("{}", firstword);
}

通过代码看切片的用法

  1. 切片通过两个中括号,并使用【 . . 】表示切片的获取范围
  2. 符号【. .】的前后加上限制范围便可以直观的表示切片的具体的开始位置和结束位置,默认左开右闭。
  3. 同时,如果切片的范围起始于所有者的head或者结束语所有者的尾,那么前后的数字都可以省略。

通过Rust对切片的定义,以及所有权的描述,可以判断出上述代码有什么问题?
s.clear()无法通过编译,因为firstword是不可变引用。

切片的优点

首先切片是引用,效率高不复制,其次,我们对变量做引用可以更好的保证数据的可靠性和安全性,不至于在数据源已经释放的情况下,依然使用index对数据进行处理。使用语法也比较方便。

Rust的字符串切片

字符串切片使用&str表示,&str也可以表示字符串字面量,&str 字符串切片本身就是不可变的引用,这样就解释了为什么Rust 的字符串字面量 不可变。同时一般有经验的Rustman 都会一般都会使用&str作为方法参数,因为这样既可以传String(因为隐式的deref)也可以传字面量。


总结

关于切片对于如Vec也是一样的,不在展开赘述。

;