Rust开发——切片(slice)类型

1、什么是切片

在 Rust 中,切片(slice)是一种基本类型和序列类型。在 Rust 官方文档中,切片被定义为“对连续序列的动态大小视图”。

但在rust的Github 源码中切片被定义如下:

切片是对一块内存的视图,表示为指针和长度。

其实这个定义更有帮助。从这里的定义可以知道,切片是一个“宽指针”(fat pointer)。所以基本上,当创建一个数组的切片时,切片包含以下内容:

  • 指向数组中切片起始元素地址的指针
  • 描述切片长度的值

2、切片示例

在 Rust 中,切片可以是对支持的数组的视图,也可以是对其他序列(例如向量或字符串)的视图。如果切片是对字符串的视图,它被称为字符串切片或字符串字面量,并且通常以其借用形式 &str 出现。

以下是一个示例数组和该数组生成的两个切片:
Rust开发——切片(slice)类型_第1张图片
左边和右边展示了对中间显示的数组提供视图的两个切片。数组和切片的定义如下:

let array: [i32; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let slice1 = &array[5..10];
let slice2 = &array[3..7];

从上图可以看到,在 slice1 中,切片的指针指向数组的索引 5。slice1 的长度为 5。这意味着切片将包含数组中的 5 个元素。下面是切片相关的索引和值。切片本身的索引从 0 到 4。

右侧是 slice2。该切片的指针指向元素 3,且切片的长度为 4。

3、切片常规操作

定义一个数组,然后对数组进行切片操作:

let array: [i32; 7] = [0, 1, 2, 3, 4, 5, 6];

let slice = &array[..]; // [ 0, 1, 2, 3, 4, 5, 6 ]
let slice = &array[0..3]; // [ 0, 1, 2 ]
let slice = &array[..3]; // [ 0, 1, 2 ]
let slice = &array[2..4]; // [ 2, 3 ]
let slice = &array[2..]; // [ 2, 3, 4, 5, 6 ]

上面定义了不可变数组以及创建数组切片的几种方法。在切片定义后的注释中展示了 dbg!(slice); 的输出结果。

之后再创建一个可变的切片:

let mut array: [i32; 7] = [0, 1, 2, 3, 4, 5, 6];
let array_slice = &mut array[0..5]; // [ 0, 1, 2, 3, 4 ]

Rust开发——切片(slice)类型_第2张图片
检查切片的长度并迭代索引/值:

slice.len(); // 5

for (index, item) in slice.iter().enumerate() {
    println!("index: {:?} element {:?}", index, item);
}
/*
index: 0 element 0
index: 1 element 1
index: 2 element 2
index: 3 element 3
index: 4 element 4
*/

从切片中检索一个值:

slice[1]; // 1

切片的长度在编译时并不总是已知的。如果访问超出边界的索引值,编译器将不会保存:

slice[100];

会报如下错误:

thread ‘main’ panicked at ‘index out of bounds: the len is 5 but the index is 100’

为了安全地从切片中获取值,可以使用 get() 方法:

slice.get(2); // Some(2)
slice.get(100); // None

在切片中查找值:

slice.iter().position(|v| v == &120); // None
slice.iter().position(|v| v == &4); // Some(4)

改变切片中元素的值:

slice[0] = 100;
dbg!(slice); // [100, 1, 2, 3, 4]
dbg!(array); // [100, 1, 2, 3, 4, 5, 6]

4.对不同类型的切片进行操作

可以从数组、向量和字符串中获取切片:

let array: [i32; 4] = [0, 1, 2, 3];
let array_slice = &array[..2]; // [0, 1]
let vector = vec![1, 2, 3, 4];
let vector_slice = &vector[0..2]; // [1, 2]
let string = String::from("string slice");
let string_slice = &string[0..6]; // "string"
println!("{:?} {:?} {:?}", array_slice, vector_slice, string_slice);
// [0, 1] [1, 2] "string"

之前定义的数组和向量包含 i32 类型,之后可以创建一个同时适用于 vector_slicearray_slice 的函数:

fn return_second(n: &[i32]) {
    println!("{}", n[1]);
}
return_second(array_slice); // 1
return_second(vector_slice); // 2

字符串切片是一个 &str,因此不能将其传递给 return_second 函数。事实上,字符串切片有点特殊。在 Rust 中,所有的字符串都是 UTF-8 编码的,因此字符的大小可以不同。iter() 方法不能用在字符串切片上,相反,需要使用 chars() 方法。要从切片中取第 n 个字符,则需要使用索引 n。

let string = String::from("Rust is ");
let string_slice = &string[..];

fn return_second_char(n: &str) {
    println!("{:?}", n.chars().nth(1));
}

return_second_char(string_slice); // Some('u')

for c in string_slice.chars() {
    println!("{}", c)
}
/*
R
u
s
t

i
s


*/
for (i, c) in string_slice.chars().enumerate() {
    println!("{} {}", i, c)
}
/*
0 R
1 u
2 s
3 t
4
5 i
6 s
7
8 
*/

5.指针

Rust中的宽指针(fat pointers)与窄指针(thin pointers)是指针类型的两种概念。

  • 窄指针(Thin Pointers):指针仅包含目标内存地址信息,不包含其他附加信息。比如,裸指针 *const T*mut T 就是窄指针,它们只存储指向某个类型 T 的内存地址。

  • 宽指针(Fat Pointers):指针除了存储目标内存地址外,还包含其他信息,例如动态数组的长度。切片 &[T] 或者动态 trait 对象 &dyn Trait 就是宽指针的例子,它们除了指向内存的地址外,还存储着长度等其他信息。

宽指针包含更多的信息,但也会带来一些额外的存储开销。窄指针更加轻量,但缺乏一些额外的信息。在Rust中,切片是一种宽指针,因为它包含指向数据的指针和数据长度。

use std::mem;
let array: [i32; 500] = [0; 500];
let slice = &array[..];
let array_pointer = &array;
let slice_pointer = &slice;
let start_of_array_slice = &array[0];
println!("--------------------------------------------");
println!("array_pointer address: {:p}", array_pointer);
println!("slice_pointer address: {:p}", slice_pointer);
println!("start_of_array_slice address: {:p}", start_of_array_slice);
println!("slice occupies {} bytes", mem::size_of_val(&slice));
println!(
    "array_pointer occupies {} bytes",
    mem::size_of_val(&array_pointer)
);
println!("array occupies {} bytes", mem::size_of_val(&array));
println!("--------------------------------------------");
--------------------------------------------
array_pointer address: 0x9def68
slice_pointer address: 0x9df738
start_of_array_slice address: 0x9def68
slice occupies 16 bytes
array_pointer occupies 8 bytes
array occupies 2000 bytes
--------------------------------------------

数组的总大小为 2000 字节。整个数组的切片(宽指针)占据 16 字节。如果获取数组的指针,得到的是一个占据 8 字节的窄指针。数组指针和切片起始地址的内存地址是相同的。

你可能感兴趣的:(rust,rust,开发语言,后端,切片,slice)