【RUST学习日记】第12课 切片引用(Slice)

简介: 【RUST学习日记】第12课 切片引用(Slice)

0x00 回顾与开篇


上一篇文章介绍了向量的知识,简单介绍了向量的基础用法,数组和向量的区别等,这节再了解Rust中另一种数据类型——切片(Slice)。它跟数组和向量又存在一定的关系,那么它们之间又有什么区别呢?这节课给你答案~


PS:本节课可能会涉及到一些关于“引用”,“内存”等概念,如果你是编程初学者,可以先了解下或者略过本节课,等后面讲解了内存,引用,指针的概念后再回顾本节课。


0x01 切片的定义


什么是切片?官方文档中此类型叫做Slice。翻译成中文(如下图)名词的意思是片,薄片,动词的意思是裁,切,切片。大多数的书都翻译为切片。通过意思,大致可以猜出是从某数据上切割下来的一部分数据。


0a2653c851af460fa595bd959398a8f1.png


官方定义:Slice是对向量或者数组中部分元素序列的引用。其签名形式为&[T]&mut [T],分别叫做T类型的共享切片T类型的可修改切片。通俗易懂点说,Slice就是表示数组或者向量的一个范围Slice从严格意义上讲,应该叫做对切片的引用。由于提到切片大都是指对它的引用,所以习惯上就把“切片引用”省略为“切片”了。


示例代码如下:


let vec = vec![1, 3, 5, 7, 9];
    let array = [0, 2, 4, 6, 8];
    let vec_slice: &[i32] = &vec;
    let array_slice: &[i32] = &array;
    dbg!(vec_slice);
    dbg!(array_slice);


代码执行结果:


[src\main.rs:8] vec_slice = [
    1,
    3,
    5,
    7,
    9,
]
[src\main.rs:9] array_slice = [
    0,
    2,
    4,
    6,
    8,
]


0x02 切片在内存中的表现形式


看了上面的代码,你会发现,只要在数组和向量名称前面添加&,就可以变成切片引用。如果你使用CLion,且声明切片引用时不指定类型,你会发现vec_slice会推断为&Vec<i32>array_slice会推断为&[i32;5],如下图所示:


2d65d23f6d4748949b924e4057485923.png


当这两行代码运行时,Rust会自动把引用&Vec<i32>&[i32;5]转换为直接指向数据切片的引用。一起来看下切片引用在内存中是如何表现的:


6de278e6d6694ce5bb08e7e842b7e74b.png


image

从上图很清楚的可以看到切片在内存中由两部分组成且在栈上保存,切片的第一部分保存着指向切片的第一个元素的指针,第二部分保存着切片中元素的个数。包括上节课讲的向量是由三部分组成的,其内存中的表现形式就是vec变量。当然数组也是保存在栈上的。另外,切片的引用是一个胖指针(Fat Pointer)。关于胖指针的概念后面章节会介绍,这里了解即可。


0x03 在切片中使用范围


在切片中可以使用范围来切割数组或者向量,签名为:&数组或向量名称[范围]。切片中的范围分为以下3种:


1、前闭后开。形如:[a..b]。表示从a到b的范围,包含a不包含b。

2、0到指定位置。形如:[..b]。表示从0到b的位置,不包含b。

3、指定位置到结束。形如:[a..]。表示从a到结束,包含a。

4、全部。形如:[..]。表示从0到结束的全部。等同于直接引用。

示例代码如下:

let vec = vec![1, 3, 5, 7, 9];
    let array = [0, 2, 4, 6, 8];
    let vec1 = &vec[1..3];
    let vec2 = &vec[..2];
    let vec3 = &vec[3..];
    let vec4 = &vec[..];
    println!("vec1 => vec中下标1到下标3的 元素 {:#?}", vec1);
    println!("vec2 => vec中下标0到下标2的 元素 {:#?}", vec2);
    println!("vec3 => vec中下标3到结束的 元素 {:#?}", vec3);
    println!("vec4 => vec中下标0到结束的 元素 {:#?}", vec4);
    // 相同
    assert_eq!(&vec[..], &vec);
    let array1 = &array[1..3];
    let array2 = &array[..2];
    let array3 = &array[3..];
    let array4 = &array[..];
    println!("array1 => array中下标1到下标3的 元素 {:#?}", array1);
    println!("array2 => array中下标0到下标2的 元素 {:#?}", array2);
    println!("array3 => array中下标3到结束的 元素 {:#?}", array3);
    println!("array4 => array中下标3到结束的 元素 {:#?}", array4);
    // 相同
    assert_eq!(&array[..], &array);


代码运行结果:


vec1 => vec中下标1到下标3的 元素 [
    3,
    5,
]
vec2 => vec中下标0到下标2的 元素 [
    1,
    3,
]
vec3 => vec中下标3到结束的 元素 [
    7,
    9,
]
array1 => array中下标1到下标3的 元素 [
    2,
    4,
]
array2 => array中下标0到下标2的 元素 [
    0,
    2,
]
array3 => array中下标3到结束的 元素 [
    6,
    8,
]

assert_eq!也是一个宏,这是一个断言,断言参数里面的两个值相等。如果不相等则会抛出错误。

PS:如果在使用范围时超出了数组或者向量的长度,则会发生越界的错误。 


0x03 切片元素的访问


切片的访问与数组和向量类似,同样也会坚持切片的索引是否有效,如果超出长度则会出现错误。

示例代码如下:


let vec = [1, 3, 5, 7, 9];
    let vec_s = &vec[1..4];
    dbg!(vec_s[2]);


代码运行结果:


[src\main.rs:47] vec_s[2] = 7


0x04 切片元素的修改


想要修改切片中的元素,切片引用的原数组或者向量必须是可变的,也就是必须使用mut关键字修饰,且切片类型为可修改切片&mut [T]切片的值一旦被修改,切片引用的原数组或者向量的值同样被修改。因为切片指向的值和原数组或者向量的值是同一块内存区域(可以参考0x02中的内存图)。

示例代码如下:


// 必须mut修饰
    let mut vec = [1, 3, 5, 7, 9];
    // 声明为可修改的切片
    let vec_m = &mut vec[1..4];
    vec_m[2] = 10;
    dbg!(vec_m);
    dbg!(vec);


代码运行结果如下:


[src\main.rs:56] vec_m = [
    3,
    5,
    10,
]
[src\main.rs:57] vec = [
    1,
    3,
    5,
    10,
    9,
]


0x05 切片的常用方法


1、len() ——获取切片的长度。


2、is_empty()——判断切片是否为空,返回值为布尔型。


示例代码如下:


let vec = [1, 3, 5, 7, 9];
    let vec_s = &vec[0..0];
    println!("切片 vec_s 的长度是{} ", vec_s.len());
    println!("切片 vec_s 是空吗?{} ", vec_s.is_empty());

代码运行结果如下:

切片 vec_s 的长度是0 
切片 vec_s 是空吗?true


0x06 小结


本节简单介绍了切片,数组,向量在内存中的表现形式,着重介绍了切片的使用。如果你没有编程语言基础,那么看到这里可能会有一点儿懵,本人建议初学者可以暂时跳过本节课,之所以将切片放到这里,也算是数组和向量的一个补充知识吧。可以等后面了解了内存栈、堆、引用、借用、指针等概念再回过头来看下本节课,到那时也许就不会觉得很难理解了。

相关文章
|
2月前
|
Rust API 索引
【Rust】——切片
【Rust】——切片
18 0
|
3月前
|
Rust 安全 编译器
Rust中避免常见错误:悬挂引用与生命周期不匹配
本文深入探讨了Rust编程语言中常见的两个内存管理错误:悬挂引用和生命周期不匹配,并提供了避免这些错误的实用方法。我们将详细解释这两种错误的来源,并通过示例展示如何在Rust中通过正确的生命周期标注和借用规则来避免它们,从而确保代码的内存安全性。
|
3月前
|
JSON Rust IDE
全网最全的Rust学习资源
学习Rust过程中整理了一些学习资料分享一下。
109 1
|
4月前
|
Rust
Rust编程语言:探索性学习与实践指南
Rust编程语言:探索性学习与实践指南
35 0
|
4月前
|
Rust 编译器
【一起学Rust】Rust学习前准备——注释和格式化输出
【一起学Rust】Rust学习前准备——注释和格式化输出
37 0
|
5月前
|
存储 Rust 安全
Rust 中的引用与借用
Rust 中的引用与借用
|
5月前
|
Rust
Rust编程语言:探索性学习与实践指南
Rust编程语言:探索性学习与实践指南
31 0
|
5月前
|
Rust 前端开发 安全
致所有渴望学习Rust的人的信
致所有渴望学习Rust的人的信
|
存储 JSON Rust
【RUST学习日记】第22课 结构体(上)
【RUST学习日记】第22课 结构体(上)
|
存储 Rust JavaScript
Rust:为什么不能在同一个结构体中存储一个值和对该值的引用?(修改版)
基本把下面问题这个搞明白,就能彻底明白 Rust 语言的生命周期是怎么回事了。简而言之,生命周期不会改变你的代码,是你的生命控制生命周期,而不是生命周期在控制你的代码。换言之,生命周期是描述性的,而不是规定性的。
141 0