Rust的Slice切片


Slice: 切片。slice 允许你 引用集合中一段连续的元素序列,而 不用引用整个集合。slice 是一类引用,所以它没有所有权。

Slice的引入

编写一个函数,该函数接收一个用空格分隔单词的字符串,并返回在该字符串中找到的第一个单词。如果函数在该字符串中并未找到空格,则整个字符串就是一个单词,所以应该返回整个字符串。

我们尝试不适用Slice,来完成这个函数,看看会有什么问题:

fn main() {

    let mut str=String::from("abcdef Hello ");
    let num=second_world(&str);
    //获得一个索引
    
    println!("{}",num);

}

fn first_world(string:&String)->usize
{
    //其实x就是索引值,elem就是这个字符所对应的ASCII码
    let str=string.as_bytes();
    for (x,&elem) in str.iter().enumerate()
    {
        if elem==b' '       //b' '的ASCII码为32 ,判断每个字符是否为32,如果遇到了空格,则退出 
        {
            return x;
        }
    }
    string.len()
}

解释:

  • 我们使用引用作为函数的参数,返回一个索引下标
  • as_bytes:将string转换为字节,需要用 as_bytes 方法将 String 转化为字节数组
  • 使用 iter 方法在字节数组上创建一个迭代器
  • 只需知道 iter 方法返回集合中的每一个元素,而 enumerate 包装了 iter 的结果,将这些元素作为元组的一部分来返回。enumerate 返回的元组中,第一个元素是索引,第二个元素是集合中元素的引用。这比我们自己计算索引要方便一些。

编译运行:
在这里插入图片描述
第一个空格出现的位置是6,即,我们可以通过循环遍历0-6个元素,这样就找到了我们需要的第一个空格前的单词

但是,当我们使用clear释放String字符串时:

str.clear();

函数返回的下标仍然存在num,但是字符串str已经不复存在了,因为它被clear了,是一个空字符串。num和str分属于不同的领域,因为 num与 str 状态完全没有联系,所以 num仍然包含值 6。可以尝试用值 6 来提取变量 str 的第一个单词,不过这是有 bug 的,因为在我们将 6 保存到 str 之后 s 的内容已经改变了。
但是这时候,编译器不会出错,但是,我们已经无法访问了字符串了

使用Slice

看下面的代码:

fn main() {
    let s = String::from("hello world");

    let hello = &s[0..5];
    let world = &s[6..11];
}

不同于String的引用,hello是一部分string的引用,它由[start_index…end_index]组成,start_index是切片的起始位置,end_index是切片的终点位置(不包括这个终点位置),切片的长度等于终点位置减去起始位置
在这里插入图片描述
可知。&str表示一部分String的切片。

当我们使用切片求解得到第一个空格前的单词:

fn main() {
    let mut str=String::from("abcdef Hello ");
    let num=second_world(&str);
    //str.clear();
    println!("{}",num);
}

fn second_world(string:&String)->&str
{
    let len=string.as_bytes();
    for (i,&elem) in len.iter().enumerate()
    {
        if elem==b' '
        {
            //到达空格,返回在这以前的单词
            return &string[..i];
        }
    }
    //没有找到
    &string[..]    //原地返回
}

注意:

  • 函数的返回值: 字符串 slice”的类型声明写作 &str
  • 我们返回一个字符串 slice,它使用字符串的开始和空格的索引作为开始和结束的索引。
    在这里插入图片描述
    编译运行:我们就得到了切片的字符串
    如果,我们像上面那样,清除str:
fn main() {
    let mut str=String::from("abcdef Hello ");
    let num=second_world(&str);
    str.clear();
    println!("{}",num);
}

那么就会发现,编译器会直接给我们报错:
在这里插入图片描述
说的是什么意思?
大概是说,我们的函数返回后得到一个切片,切片是一个不可变引用&str,赋予了num,然后我们使用clear时,可以看到,clear其实获取了一个可变的引用,上一节我们讲到不可变引用与可变引用不能同时存在,尤其是clear的可变引用还在生命周期内,我们在下面的println!函数中调用了打印num,Rust 不允许 clear 中的可变引用和 word 中的不可变引用同时存在,因此编译失败。
在这里插入图片描述
所以我们就知道了我们在此后不能打印字符串了,Rust 不仅使得我们的 API 简单易用,也在编译时就消除了一整类的错误!

其他Slice

Slice字符串字面值

现在知道 slice 了,我们就可以正确地理解字符串字面值了:

let s = "Hello, world!";

在这里插入图片描述

这里 s 的类型是 &str:它是一个指向二进制程序特定位置的 slice。这也就是为什么字符串字面值是不可变的;&str 是一个不可变引用

Slice作为函数参数

fn second_world(string:&str)->&str

相对于我们上面编写的&String版本的函数参数,此版本的&str更受我们的欢迎。因为函数参数是一个切片,我们就可以指定传入的参数的大小:(注意:如果参数是String类型,则无法切片)。

fn first_word(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 my_string = String::from("hello world");

    // `first_word` 适用于 `String`(的 slice),整体或全部
    let word = first_word(&my_string[0..6]);
    let word = first_word(&my_string[..]);
    // `first_word` 也适用于 `String` 的引用,
    // 这等价于整个 `String` 的 slice
    let word = first_word(&my_string);

    let my_string_literal = "hello world";

    // `first_word` 适用于字符串字面值,整体或全部
    let word = first_word(&my_string_literal[0..6]);
    let word = first_word(&my_string_literal[..]);

    // 因为字符串字面值已经 **是** 字符串 slice 了,
    // 这也是适用的,无需 slice 语法!
    let word = first_word(my_string_literal);
}

注意:字符串字面值已经是Slice了,无需使用&符号,而函数参数直接对应&str切片类型。

获取数组切片

#![allow(unused)]
fn main() {
let a = [1, 2, 3, 4, 5];

let slice = &a[1..3];

assert_eq!(slice, &[2, 3]);
}

这个 slice 的类型是 &[i32]。它跟字符串 slice 的工作方式一样,通过存储第一个集合元素的引用和一个集合总长度。

下期预告:结构体

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yuleo_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值