Rust语言特性——借用和切片

B站影视 欧美电影 2025-10-15 17:03 2

摘要:Rust 支持对所有权的出借 borrowing。当把一个具有所有权的变量传递给函数时,就是把所有权借用给函数的参数,当函数返回后则自动收回所有权。

Rust 支持对所有权的 出借 borrowing。当把一个具有所有权的变量传递给函数时,就是把所有权借用给函数的参数,当函数返回后则自动收回所有权。

1、基本概念

下面这段代码中,我们定义了两个函数 main 和 print_vector ,前者是应用程序的入口函数,而后者则用于输出一个 向量。

我们在 main 函数中定义了一个向量,同时将这个向量传递给 print_vector 作为参数。 因为参数的传递会触发所有权的转让。因此将 v 传递给 print_vector 函数时,数据的所有权就从 v 转让到了 参数 x 上。

fn main{ let v = vec![10,20,30]; // 声明一个向量,变量 v 具有数据的所有权 print_vector(v); println!("{}",v[0]); // 这行会报错}fn print_vector(x:Vec){ println!("Inside print_vector function {:?}",x);}

但函数返回时我们并没有把 x 对数据的所有权转让回 v 变量,因此上面这段代码编译的时候编译的时候就会报错了。

error[E0382]: borrow of moved value: `v` --> src/main.rs:5:18 |3 | let v = vec![10,20,30]; // 声明一个向量,变量 v 具有数据的所有权 | - move occurs because `v` has type `std::vec::Vec`, which does not implement the `Copy` trait4 | print_vector(v); | - value moved here5 | println!("{}",v[0]); // 这行会报错 | ^ value borrowed here after moveerror: aborting due to previous error

Rust 语言中,借用 就是一个函数中将一个变量传递给另一个函数作为参数暂时使用。

同时,Rust 也引用了自动 还 的概念,就是要求函数的参数离开其作用域时需要将 所有权 还给当初传递给他的变量,这个过程,我们需要将函数的参数定义为 &variable_name,同时传递参数时,需要传递 &variable_name。

站在 C++ 语言的角度考虑,就是将 函数的参数定义为引用,同时传递变量的引用。

有了 借用 Borrowing 也就是引用的概念后,我们只要修改两处就能让上面的代码运行起来

fn print_vector(x:&Vec){ // 1. 第一步,定义参数接受一个引用 println!("Inside print_vector function {:?}",x);}fn main{ let v = vec![10,20,30]; // 声明一个向量,变量 v 具有数据的所有权 print_vector(&v); // 第二步,传递变量的引用给函数 println!("{}",v[0]); }

2、可变引用

借用 Borrowing 或者说引用默认情况下是只读的,也就是我们不能修改引用的的变量的值。

fn add_one(e: &i32) { *e+= 1;}fn main { let i = 3; println!("before {}",i); add_one(&i); println!("after {}", i);}

编译运行以上 Rust 代码,输出结果如下

error[E0594]: cannot assign to `*e` which is behind a `&` reference --> src/main.rs:2:4 |1 | fn add_one(e: &i32) { | ---- help: consider changing this to be a mutable reference: `&mut i32`2 | *e+= 1; | ^^^^^^ `e` is a `&` reference, so the data it refers to cannot be writtenerror: aborting due to previous error

我们尝试在函数 add_one 将引用的变量 +1 但却编译失败了。

而失败的原因,就像错误信息里说的那样,引用 默认情况下是不可编辑的。

Rust 中,要让一个变量可编辑,唯一的方式就是给他加上 mut 关键字。

因此,我们可以将上面的代码改造下,改成下面这样

fn add_one(e: &mut i32) { *e+= 1;}fn main { let mut i = 3; println!("before {}",i); add_one(&mut i); println!("after {}", i);}before 3after 4

从上面的代码中可以看出: 借用 Borrowing 或者说引用的变量如果要变更,必须符合满足三个要求:

① 变量本身是可变更的,也就是定义时必须添加 mut 关键字。

② 函数的参数也必须定义为可变更的,加上 借用 Borrowing 或者说引用,也就是必须添加 &mut 关键字。

③ 传递 借用 Borrowing 或者说引用也必须声明时 可变更传递,也就是传递参数时必须添加 &mut 关键字。

3、字符串的可变引用

我们操作的是基础的数据类型,如果是堆上分配的变量又会怎么样呢 ? 比如字符串。

其实堆上分配的变量的 借用 Borrowing 或者说引用跟基础类型的变量一样

fn main { let mut name:String = String::from("TutorialsPoint"); display(&mut name); // 传递一个可变引用 println!("The value of name after modification is:{}",name);}fn display(param_name:&mut String){ println!("param_name value is :{}",param_name); param_name.push_str(" Rocks"); // 修改字符串,追加一些字符}param_name value is :TutorialsPointThe value of name after modification is:TutorialsPoint Rocks

一个 切片(slice) 就是指向一段内存的指针。

因此切片可用于访问内存块中连续区间内的数据。

一般情况下,能够在内存中连续区间存储数据的数据结构有: 数组 array、向量 vector、字符串 string。

也就是说,切片 可以和数组、向量、字符串一起使用,它使用 数字索引 (类似于数组的下标索引)来访问它所指向的数据。

例如,切片 可以和字符串一起使用,切片 可以指向字符串中连续的一部分。这种 字符串切片 实际上是指向字符串的指针。因为是指向字符串的连续区间,所以我们要指定字符串的开始和结束位置。

访问切片内容的时候,下标索引是从 0 开始的。

切片 的大小是运行时才可知的,并不是数组那种编译时就必须告知的。

1、定义切片的语法

定义一个切片的语法格式如下

let sliced_value = &data_structure[start_index..end_index]

定义切片时的几个注意事项:

① [start_index..end_index] 是一个左闭右开区间 [start_index,end_index)

② 要注意区间的语法两个点 .. 。

③ start_index 的最小值为 0,这是因为数组、向量、字符串它们的下标访问方式也是从 0 开始的。

④ end_index 的最大值是数组、向量、字符串的长度。

⑤ end_index 所表示的索引的字符并不包含在切片里面。

2、切片示例

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

以下是一个示例数组和该数组生成的两个切片:

左边和右边展示了对中间显示的数组提供视图的两个切片。数组和切片的定义如下:

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、切片作为函数参数

切片还可以作为函数的参数。

使用切片可以把数组、向量、字符串中的连续子集通过引用的方式传递给函数。

我们先来看一段简单的代码

fn main{ let data = [10,20,30,40,50]; use_slice(&data[1..4]); //this is effectively borrowing elements for a while}fn use_slice(slice:&[i32]) { // is taking a slice or borrowing a part of an array of i32s println!("length of slice is {:?}",slice.len); println!("{:?}",slice);}

上面这段代码中

① 声明了两个函数 main 和 use_slice,后者接受一个切片并打印切片的长度。

② 首先在 main 函数中声明了一个有 5 个元素的数组。

③ 然后调用函数 use_slice 并把数组的一个切片( 从下标 1 开始到下标 3 之间的元素 )作为参数。

4、可变更切片

默认情况下 切片 是不可变更的。

虽然,看起来切片是指向原数据,但是默认情况下我们并不能改变切片的元素。

也就说默认情况下不能通过更改切片的元素来影响原数据。

但这不是绝对的,如果我们声明的原数据是可变的,同时定义切片的时候添加了 &mut 关键字,那么我们就可以通过更改切片的元素来影响原数据。

fn main{ let mut data = [10,20,30,40,50]; use_slice(&mut data[1..4]); // passes references of 20, 30 and 40 println!("{:?}",data);}fn use_slice(slice:&mut [i32]) { println!("切片的长度为:{:?}",slice.len); println!("{:?}",slice); slice[0] = 1010; // replaces 20 with 1010}

来源:美美课堂

相关推荐