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


    [易学易懂系列|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,谢谢!

    本人精通java高并发,DDD,微服务等技术实践,专注java,rust技术栈。 本人姓名郭莹城,坐标深圳,前IBM架构师、咨询师、敏捷开发技术教练,前IBM区块链研究小组成员、十多年架构设计工作经验,《区块链核心技术与应用》作者之一, 现聚焦于:区块链创投与交易所资源对接和技术咨询。 工作微信&QQ:360369487,区块链创投与交易所资源对接,加我注明:博客园+对接,技术咨询和顾问,加我注明:博客园+顾问。想学习golang和rust的同学,也可以加我微信,备注:博客园+golang或博客园+rust,谢谢!
  • 相关阅读:
    No configuration found for this host:al
    相对路径和绝对路径
    工具类学习
    JRebel没有自动部署的解决方法
    之前写了http解析高德地图时,json转对象搞了半天 , 今天同事用GSON把json转对象,一句代码就解决了,代码如下
    导入项目时遇到的问题
    解析Http请求调用高德地图的api(货车路径规划)
    二进制中的符号位的区分以及表示
    svn提交及更新时的常见问题
    JDBC 连接池下重构操作
  • 原文地址:https://www.cnblogs.com/gyc567/p/11910563.html
Copyright © 2020-2023  润新知