09_rust的切片

rust的切片

rust另一种不持有所有权的数据类型:切片(slice)
从一个例子开始:实现一个函数,输入一字符串,返回该字符串第一个单词,如果没空格,则返回整个字符串。

  • 第一种实现,先返回找到的第一个空格的位置
fn main() {
    let s = String::from("hello world");
    let ind = get_first_world(&s);
    s.clear();
    println!("{}", ind);
}

fn get_first_world(s: &String) -> usize { // 先返回空格位置
    let bytes = s.as_bytes(); // 返回值类型时 &u8
    for (i, &item) in bytes.iter().enumerate() { //iter方法会创建一个迭代器,返回索引和元素
    // enumerate方法则对iter结果进行包装,会把iter的结果作为元组的一部分进行返回,(i, &item)进行模式匹配获得结果
        if item == b' ' {
           return i;
        }
    }
    return s.len()
}

这里把函数的索引位置返回,而索引位置脱离了上下文,比如s失效后,该索引就毫无意义了。比如代码中使用了s.clear()对字符串进行清空,但ind是独立于字符串存在的,并未随着s.clear失效,如果再用这个索引则会发生错误了。所以这类函数需要时刻关注原字符串s的有效性。而这种关联特性是非常复杂的,rust提供的解决方案是字符串切片。

字符串切片

字符串切片是指向字符串中一部分内存的引用。
形式:[开始索引..结束索引]
开始索引是切片起始位置的索引,结束索引是切片终止位置的下一个索引。

fn main() {
    let s = String::from("hello world");
    let s_h = &s[0..5]; // 截取0到4位置的字符,hello
    let s_h = &s[..5]; // 与上一行等价
    let s_w = &s[6..11]; // 截取6到10索引位置的字符,world
    let s_w = &s[6..]; // 与上一行等价
    let s_w = &s[..]; // 指向整个字符串切片
}

字符串切片的范围索引必须发生在有效的UTF-8字符边界内,若尝试一个多字节的字符中创建字符串切片,程序报错退出。
再看之前例子

fn main() {
    let s = String::from("hello world");
    let ind = get_first_world(&s); // 不可变借用点
    // s.clear(); // 此时则报错:cannot borrow 's' as mutable because it is also borrowed as immutable
    println!("{}", ind);
}

fn get_first_world(s: &String) -> &str { // 返回值是字符串切片
    let bytes = s.as_bytes(); // 返回值类型时 &u8
    for (i, &item) in bytes.iter().enumerate() { //iter方法会创建一个迭代器,返回索引和元素
    // enumerate方法则对iter结果进行包装,会把iter的结果作为元组的一部分进行返回,(i, &item)进行模式匹配获得结果
        if item == b' ' {
           return &s[..i];
        }
    }
    return s[..]
}

字符串字面值是切片

字符串字面值被直接存储在二进制程序中。
比如

fn main() {
    let s = "test"; // 类型&str
}

前面的例子,get_first_world(s: &String) -> &str {,可使用&str作为参数类型,即可同时接收String和&str类型的参数。

fn main() {
    let s = String::from("hello world");
    let ind = get_first_world(&s[..]); // 不可变借用点
    let s2 = "hello world";
    let ind = get_first_world(s2); // 不可变借用点
}

fn get_first_world(s: &Str) -> &str { // 返回值是字符串切片
    let bytes = s.as_bytes(); // 返回值类型时 &u8
    for (i, &item) in bytes.iter().enumerate() { //iter方法会创建一个迭代器,返回索引和元素
    // enumerate方法则对iter结果进行包装,会把iter的结果作为元组的一部分进行返回,(i, &item)进行模式匹配获得结果
        if item == b' ' {
           return &s[..i];
        }
    }
    return s[..]
}

其他类型切片

数组也可切片

fn main() {
    let s = [1, 2, 3, 4, 5];
    let sl = &s[1..3]; // 类型是&[i32],创建了一个指向起始位置的指针
}
posted @ 2023-10-17 00:57  00lab  阅读(36)  评论(0编辑  收藏  举报