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

Borrowing

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

什么是借用?

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

 let a = [1, 2, 3];

let b = a;

println!("{:?} {:?}", a, b); *// [1, 2, 3] [1, 2, 3]*

let a = vec![1, 2, 3];

let b = a;

println!("{:?} {:?}", a, b); *// Error; use of moved value: `a`*

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

我们改成以下代码:

 let a = vec![1, 2, 3];

let b = **&**a;//这里加了一个符号:**&**,表示借用

println!("{:?} {:?}", a, b); *// correct*

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

这里就出来一个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 )。

请看如下代码:

fn main() {
 let mut a = vec![1, 2, 3];
 let b = &mut a;  // &mut borrow of `a` starts here
                  // ⁝
 // some code     // ⁝
 // some code     // ⁝
}                  // &mut borrow of `a` ends here


fn main() {
 let mut a = vec![1, 2, 3];
 let b = &mut a;  // &mut borrow of `a` starts here
 // some code

 println!("{:?}", a); // trying to access `a` as a shared borrow, so giving an error
}                  // &mut borrow of `a` ends here


fn main() {
 let mut a = vec![1, 2, 3];
{
   let b = &mut a;  // &mut borrow of `a` starts here
   // any other code
}                  // &mut borrow of `a` ends here

 println!("{:?}", a); // allow borrowing `a` as a shared borrow
}

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

像这段代码 :

fn main() {  
   let mut a = vec![1, 2, 3];
   let b = &mut a; // &mut borrow of `a` starts here // some code

   println!("{:?}", a); // trying to access `a` as a shared borrow, so giving                         //an error
} // &mut borrow of `a` ends here

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

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

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

那要怎么办?

加上大括号“{}”。

如下 :

 let mut a = vec![1, 2, 3];
{
   let b = &mut a;  // &mut borrow of `a` starts here
   // any other code
}                  // &mut borrow of `a` ends here

 println!("{:?}", a); // allow borrowing `a` as a shared borrow

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

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

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

共享借用:

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


fn main() {
   let a = vec![1, 2, 3];
   let b = get_first_element(&a);

   println!("{:?} {}", a, b); // [1, 2, 3] 1
}

fn get_first_element(a: &Vec<i32>) -> i32 {
   a[0]
}

第一段代码:

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

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

第二段代码:

fn main() {
   let a = vec![1, 2, 3];
   let b = get_first_element(&a);

   println!("{:?} {}", a, b); // [1, 2, 3] 1
}

fn get_first_element(a: &Vec<i32>) -> i32 {
   a[0]
}

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

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

fn get_first_element(a: &Vec<i32>) -> i32 {

a[0]=9;

a[0]

}

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

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

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

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

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

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

fn main() {  
let mut a = vec![1, 2, 3];
let b = get_first_element(&mut a);//从函数get_first_element返回后,把数据所有权还给a


println!("{:?} {}", a, b); // [9, 2, 3] 9


}

fn get_first_element(g: &mut Vec<i32>) -> i32 { //开始借用 a的数据
 g[0]=9;
 g[0]

}//结束借用 a的数据,返回到main函数后,把数据所有权还给a

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

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

所以打印结果为:

[9, 2, 3] 9

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

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


fn main() {
   let mut a = [1, 2, 3];
  {
       let b = &mut a;
       b[0] = 4;
  }

   println!("{:?}", a); // [4, 2, 3]
}


fn main() {
   let mut a = vec![1, 2, 3];
   let b = change_and_get_first_element(&mut a);

   println!("{:?} {}", a, b); // [4, 2, 3] 4
}

fn change_and_get_first_element(a: &mut Vec<i32>) -> i32 {
   a[0] = 4;
   a[0]
}

所以,我们结合所有权(Ownership)借用(Borrowing)两个概念来理解。

得出来一个重要结论:

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

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

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

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

以上,希望对你有用。

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

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

[易学易懂系列|rustlang语言|零基础|快速入门|(4)|借用Borrowing]的更多相关文章

  1. [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具]

    [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具] 项目实战 实战5:实现BTC价格转换工具 今天我们来开发一个简单的BTC实时价格转换工具. 我们首先 ...

  2. [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]

    [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链] 项目实战 实战4:从零实现BTC区块链 我们今天来开发我们的BTC区块链系统. 简单来说,从数据结构的 ...

  3. [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)] 项目实战 实战3:Http服务器 我们今天来进一步开发我们的Http服务器,用多线程实现. 我 ...

  4. [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)] 项目实战 实战2:命令行工具minigrep 我们继续开发我们的minigrep. 我们现 ...

  5. [易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)] 项目实战 实战2:命令行工具minigrep 有了昨天的基础,我们今天来开始另一个稍微有点 ...

  6. [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏]

    [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏] 项目实战 实战1:猜数字游戏 我们今天来来开始简单的项目实战. 第一个简单项目是猜数字游戏. 简单来说,系统给了 ...

  7. [易学易懂系列|rustlang语言|零基础|快速入门|(5)|生命周期Lifetime]

    [易学易懂系列|rustlang语言|零基础|快速入门|(5)] Lifetimes 我们继续谈谈生命周期(lifttime),我们还是拿代码来说话: fn main() { let mut a = ...

  8. [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]

    [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...

  9. [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针]

    [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针] 实用知识 智能指针 我们今天来讲讲Rust中的智能指针. 什么是指针? 在Rust,指针(普通指针),就是保存内存地址的值 ...

  10. [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]

    [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理] 实用知识 错误处理 我们今天来讲讲Rust中的错误处理. 很多语言都有自己的错误处理方式,比如,java是异常处理机制. ...

随机推荐

  1. 线程池的管理类MyThreadPoolManager

    import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.ut ...

  2. 编辑器UEditor入门学习

    优点:非常使用的富文本编辑器,对比于之前使用的summernote,比前者多出了更多的字体图标 废话少说,直接步骤: 1.导入资源(全部放在单独的文件下即可,下图为“UEditor”文件夹) 2.引用 ...

  3. opencv、numpy中矩阵转置,矩阵内的固定位置相应的坐标变换

    opencv.numpy中矩阵转置,矩阵内的固定位置相应的坐标变换

  4. SSH整合项目----在线商城

    1.1 网上商城需求分析 1.1.1    前台:用户模块. 注册: * JS非空校验.(不是必须) * Struts2数据校验:在注册Action类同一包下新建UserAction-user_reg ...

  5. LeetCode.1010-歌曲总长度可被60整除的对数

    这是小川的第377次更新,第405篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第239题(顺位题号是1010).在歌曲列表中,第i首歌曲的持续时间为[i]秒. 返回其总 ...

  6. Scratch少儿编程系列:(十一)Scratch编程之简单见解

    一.Scratch官网的说明     With Scratch, you can program your own interactive stories, games, and animations ...

  7. 【VS开发】【智能语音处理】DTW算法(语音识别)

    DTW主要是应用在孤立词识别的算法,用来识别一些特定的指令比较好用,这个算法是基于DP(动态规划)的算法基础上发展而来的.这里介绍语音识别就先介绍下语音识别的框架,首先我们要有一个比对的模版声音,然后 ...

  8. javascript 数据类型之数值转换

    数值转换 一.有3个函数可以把非数值转换为数值: Number() parse Int() parse Float() 说明: 1.Number()可以用于任何数据类型,强转类型,如果不能把指转成数值 ...

  9. ucloud建新主机

    系统盘默认20G,可调到40不增加费用.需建好主机后关机才能更改. root密码按统一的设 设好主机名,选好分组

  10. Leetcode 35.搜索插入位置 By Python

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会被按顺序插入的位置. 你可以假设数组中无重复元素. 示例 1: 输入: [1,3,5,6], 5 输 ...