[易学易懂系列|rustlang语言|零基础|快速入门|(4)|借用Borrowing]

释放双眼,带上耳机,听听看~!

[易学易懂系列|rustlang语言|零基础|快速入门|(4)

]

Borrowing

继续讲讲另一个重要的概念:借用(borrowing),

什么是借用?

我们先来看前一文章([
易学易懂系列|rustlang语言|零基础|快速入门|(3)]
)的代码 :


1
2
3
4
5
6
7
8
9
10
11
12
1let a = [1, 2, 3];  
2​  
3let b = a;  
4​  
5println!("{:?} {:?}", a, b); *// [1, 2, 3] [1, 2, 3]*  
6​  
7let a = vec![1, 2, 3];  
8​  
9let b = a;  
10​  
11println!("{:?} {:?}", a, b); *// Error; use of moved value: `a`*
12

我们从上篇文章知道,第二段代码会报错,那怎么才能不报错呢?

我们改成以下代码:


1
2
3
4
5
6
1let a = vec![1, 2, 3];  
2​  
3let b = **&**a;//这里加了一个符号:**&**,表示借用  
4​  
5println!("{:?} {:?}", a, b); *// correct*
6

现在可以顺利通过傲娇的编程器女王的检查了!这就是“借用”的功效!

这里就出来一个rust语言的概念,叫借用(borrowing)。

来看下定义:

英语:
Borrow (verb)
To receive something with the promise of returning it.

翻译成中文:出来混,借了东西,迟早要还的!

那借用又分两类型:

1.共享借用(
Shared Borrowing

(&T)

数据可以借用给一个或多个用户(或线程),但只准一个用户修改。

2.可变借用(
Mutable Borrowing

(&mut T)

数据可以借用给一个用户,并只准这个用户修改,同时不准其他用户访问。

借用规则如下 :

1.数据同一时间,只能是其中一种借用,要么是共享借用(
Shared Borrowing

(&T)
),要么是可变借用(
Mutable Borrowing

(&mut T)
)。

2.借用概念适用于复制类型(Copy type )和移动类型(
Move type
)。

请看如下代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1fn main() {  
2 let mut a = vec![1, 2, 3];  
3 let b = &mut a;  //  &mut borrow of `a` starts here  
4                  //  ⁝  
5 // some code     //  ⁝  
6 // some code     //  ⁝  
7}                  //  &mut borrow of `a` ends here  
8​  
9​  
10fn main() {  
11 let mut a = vec![1, 2, 3];  
12 let b = &mut a;  //  &mut borrow of `a` starts here  
13 // some code  
14​  
15 println!("{:?}", a); // trying to access `a` as a shared borrow, so giving an error  
16}                  //  &mut borrow of `a` ends here  
17​  
18​  
19fn main() {  
20 let mut a = vec![1, 2, 3];  
21{  
22   let b = &mut a;  //  &mut borrow of `a` starts here  
23   // any other code  
24}                  //  &mut borrow of `a` ends here  
25​  
26 println!("{:?}", a); // allow borrowing `a` as a shared borrow  
27}
28

从上面代码,我们可以看出,借用,也是有“生命周期”的。

像这段代码 :


1
2
3
4
5
6
7
1fn main() {    
2   let mut a = vec![1, 2, 3];  
3   let b = &mut a; // &mut borrow of `a` starts here  // some code  
4​  
5   println!("{:?}", a); // trying to access `a` as a shared borrow, so giving                          //an error  
6} // &mut borrow of `a` ends here
7

为什么会报错?因为当最后一行代码:


1
2
1println!("{:?}", a);
2

要访问a时,a对数据的所有权,已经借用给b了。a已经没有数据所有权。所以报错。

那要怎么办?

加上大括号“{}”。

如下 :


1
2
3
4
5
6
7
8
1let mut a = vec![1, 2, 3];  
2{  
3   let b = &mut a;  //  &mut borrow of `a` starts here  
4   // any other code  
5}                  //  &mut borrow of `a` ends here  
6​  
7 println!("{:?}", a); // allow borrowing `a` as a shared borrow
8

加上{}后,把借用,限定在大括号内,大括号结束后,b会把
所有权还给
a。

这时,a对数据有了所有权,就可以访问数据了!

那共享借用和可变借用有什么区别呢,请看代码如下 :

共享借用:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1fn main() {  
2   let a = [1, 2, 3];  
3   let b = &a;  
4   println!("{:?} {}", a, b[0]); // [1, 2, 3] 1  
5}  
6​  
7​  
8fn main() {  
9   let a = vec![1, 2, 3];  
10   let b = get_first_element(&a);  
11​  
12   println!("{:?} {}", a, b); // [1, 2, 3] 1  
13}  
14​  
15fn get_first_element(a: &Vec<i32>) -> i32 {  
16   a[0]  
17}
18

第一段代码:


1
2
3
4
5
6
1fn main() {  
2   let a = [1, 2, 3];  
3   let b = &a;  
4   println!("{:?} {}", a, b[0]); // [1, 2, 3] 1  
5}
6

这里a借用给了b,为什么a还可以访问呢?因为a的类型是数组,是基本类型。这是复制类型,共享借用,只借用复制数据。所以,原来a还是拥有对原始数据的所有权。

第二段代码:


1
2
3
4
5
6
7
8
9
10
11
1fn main() {  
2   let a = vec![1, 2, 3];  
3   let b = get_first_element(&a);  
4​  
5   println!("{:?} {}", a, b); // [1, 2, 3] 1  
6}  
7​  
8fn get_first_element(a: &Vec<i32>) -> i32 {  
9   a[0]  
10}
11

这里定义一个函数,get_first_element,返回值为数组中的第一个值。b从函数中得到值1。没有什么问题。

现在我们修改一下函数get_first_element的代码,如下 :


1
2
3
4
5
6
7
8
1fn get_first_element(a: &Vec<i32>) -> i32 {  
2​  
3a[0]=9;  
4​  
5a[0]  
6​  
7}
8

这时,傲娇的编译器女王,又扔出一个错误给你:


1
2
3
4
5
1fn get_first_element(a: &Vec<i32>) -> i32 {  
2   | --------- help: consider changing this to be a mutable reference: `&mut std::vec::Vec`  
3   | a[0]=9;  |  
4     ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
5

这些错误信息很清楚地告诉你:

你现在是共享借用,共享借用只能共享数据,不能修改!(这里的真实含义是:
共享借用了数据,没有所有权,如果没有所有权,就没有修改权,只有拥有所有权,才有修改权!
)

那要修改怎么办?用可变借用啊!

把代码修改为如下就可以了:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1fn main() {    
2let mut a = vec![1, 2, 3];  
3let b = get_first_element(&mut a);//从函数get_first_element返回后,把数据所有权还给a  
4​  
5​  
6println!("{:?} {}", a, b); // [9, 2, 3] 9  
7​  
8​  
9}  
10​  
11fn get_first_element(g: &mut Vec<i32>) -> i32 { //开始借用 a的数据  
12 g[0]=9;  
13 g[0]  
14​  
15}//结束借用 a的数据,返回到main函数后,把数据所有权还给a
16

以上代码,已经把
不可变
变量a改为
可变
变量,
共享变量
&a改为
可变共享
&mut a。

在上面的代码中,a所绑定的数据的
所有权(ownership)
已经
移动

move
),也就是说数据
所有权(ownership)
已经从a转交到函数get_first_element的参数变量g,在函数get_first_element内,修改了数据的内容,然后返回main函数,并把数据所有权
还给
绑定变量a,这时数据内容已经更新。

所以打印结果为:


1
2
1[9, 2, 3] 9
2

再看看可变借用完整例子:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1fn main() {  
2   let mut a = [1, 2, 3];  
3   let b = &mut a;  
4   b[0] = 4;  
5   println!("{:?}", b); // [4, 2, 3]  
6}  
7​  
8​  
9fn main() {  
10   let mut a = [1, 2, 3];  
11   {  
12       let b = &mut a;  
13       b[0] = 4;  
14   }  
15​  
16   println!("{:?}", a); // [4, 2, 3]  
17}  
18​  
19​  
20fn main() {  
21   let mut a = vec![1, 2, 3];  
22   let b = change_and_get_first_element(&mut a);  
23​  
24   println!("{:?} {}", a, b); // [4, 2, 3] 4  
25}  
26​  
27fn change_and_get_first_element(a: &mut Vec<i32>) -> i32 {  
28   a[0] = 4;  
29   a[0]  
30}
31

所以,我们结合
所有权(Ownership)

借用(Borrowing)
两个概念来理解。

得出来一个重要结论:

没有所有权,就没有修改权,只有拥有所有权,才有修改权

共享借用,不会转交数据所有权,所以借用者,没有修改权,借用者归还数据所有权后,数据内容不变。

可变借用,会转交数据所有权,所以借用者,拥有修改权,借用者归还数据所有权后,数据内容可能已经改变

4.这里的“借用”,其实可以跟“引用”划上等号,共享借用,也就是共享引用(或不可变引用),可变借用,也就是可变引用,但它们都是跟数据所有权结合一起的。

以上,希望对你有用。


1
2
1如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
2

本人精通java高并发,DDD,微服务等技术实践,以及python,golang技术栈。 本人姓名郭莹城,坐标深圳,前IBM架构师、咨询师、敏捷开发技术教练,前IBM区块链研究小组成员、十四年架构设计工作经验,《区块链核心技术与应用》作者之一, 现有成熟团队提供区块链开发相关业务(公链,交易所,钱包,Dapp,智能合约)。 工作微信&QQ:360369487,交易所开发与区块链钱包开发业务,加我注明:博客园+开发,想学习golang和rust的同学,也可以加我微信,备注:博客园+golang或博客园+rust,谢谢!

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C/C++内存泄漏及检测

2022-1-11 12:36:11

安全运维

将MySQL去重操作优化到极致之三弹连发(二):多线程并行执行

2021-12-11 11:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索