• Rust 模式匹配中的& 和 ref


    一、Rust & 和 ref

    1.Rust的ref有什么用

    根据Rust官方文档https://doc.rust-lang.org/std/keyword.ref.html

    Rust ref 主要用在模式匹配match的所有权问题中。

    ref在 Rust中,也是声明一个指针类型变量,进一步说明ref和&在其它方面区别,我们下一篇再说。

    Rust的模式匹配,可以发生在函数参数match

    1. 函数参数
    fn foo(x: String) {//String::from("test")的所有权已经移交给了x
      println!("{}",x);
      // 代码
    }
    
    fn main() {
        let mut s=String::from("test");
        foo(s);
        println!("{}",s);//s的所有权已经丢失,所以不能使用了,此处出错
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    特别注意一下,函数传参也会交出所有权
    2.match

    fn main() {
        let x = String::from("test");
    
        match x {
            y => println!("{}", y),// String::from("test")的所有权移动给了y
            _ => println!("Default case, x = {:?}", x),
        }
    
        println!("at the end: x = {:?}", x);//x的所有权已经丢失,不能再使用。
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    特别注意一下,match的模式匹配是会交出所有权的。

    除了使用&引用借用来避免交出所有权的问题,在模式匹配中,我们可以使用**&或ref**来避免交出所有权,match中的ref不再遵循借用规则。那么前面的代码我们可以修改为。

    1. 函数参数使用**&避免**交出所有权
    fn foo(x: &mut String) {//
      println!("{}",x);
      // 代码
    }
    
    fn main() {
        let mut s=String::from("test");
        foo(&mut s);//(可变/不可变)引用,不拥有所有权
        println!("{}",s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码运行通过

        Finished release [optimized] target(s) in 0.20s
         Running `target/release/world_hello`
    test
    test
    
    • 1
    • 2
    • 3
    • 4

    2、match使用ref避免交出所有权

    fn main() {
        let x = String::from("test");
    
        match x {
            ref y => println!("{}", y),// 其实在match中,不是定义一个变量,而是声明一个变量,ref是进一步声明,y是一个引用,但是不能够&y,因为我们不能够在match中声明y的具体类型。
            _ => println!("Default case, x = {:?}", x),
        }
    
        println!("at the end: x = {:?}", x);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码运行通过

        Finished release [optimized] target(s) in 0.20s
         Running `target/release/world_hello`
    test
    test
    
    • 1
    • 2
    • 3
    • 4

    其实这里你可能会问,为什么在match中,不使用&借用避免所有权移动的问题,这是因为match本身机制导致的。

    在match中,我们没有机会声明变量类型,不能用&修饰匹配的变量

    当然,你非要在**match使用&**来避免移交所有权的问题,我们可以这样做

    fn main() {
      let x = String::from("test");
    
      match &x {//将引用定义在这里
          y => println!("{}", y),//这里不能够写成&y => println!("{}", y),
          _ => println!("Default case, x = {:?}", x),
      }
    
      println!("at the end: x = {:?}", x);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码运行也是成功的

        Finished release [optimized] target(s) in 0.22s
         Running `target/release/world_hello`
    test
    at the end: x = "test"
    
    • 1
    • 2
    • 3
    • 4

    我们再来看一个骚的

    
    // 修复错误,尽量少地修改代码
    // 不要移除任何代码行
    fn main() {
        let mut v = String::from("hello,");
        let r = &mut v;
    
        match r {
           &mut value => value.push_str(" world!") 
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    前面我们说了 ,我们不能声明匹配变量,也就是value,那么**&mut 是肯定不能用**的,其实第一版修改,我们可以改成

    
    // 修复错误,尽量少地修改代码
    // 不要移除任何代码行
    fn main() {
      let mut v = String::from("hello,");
      let r = &mut v;
    
      match r {
         value => value.push_str(" world!") //这样其实value就是可变引用,但是&mut v本身的所有权被value拿走了
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    根据代码注释,如果我们稍加修改就会发现问题

    
    // 修复错误,尽量少地修改代码
    // 不要移除任何代码行
    fn main() {
      let mut v = String::from("hello,");
      let r = &mut v;
    
      match r {
         value => value.push_str(" world!") 
      }
      println!("{}",r);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运行代码

    error[E0382]: borrow of moved value: `r`
      --> src/main.rs:11:17
       |
    6  |   let r = &mut v;
       |       - move occurs because `r` has type `&mut String`, which does not implement the `Copy` trait
    ...
    9  |      value => value.push_str(" world!") 
       |      ----- value moved here
    10 |   }
    11 |   println!("{}",r);
       |                 ^ value borrowed here after move
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这里说了,&mut v的所有权被value拿走了

    那我不想被value拿走,我们应该怎么做,那么这时候ref的作用就来了,我们可以这么修改

    
    
    // 修复错误,尽量少地修改代码
    // 不要移除任何代码行
    fn main() {
      let mut v = String::from("hello,");
      let r = &mut v;
    
      match *r {//注意这里是*r,因为r本身就是&str,如果传下去r,ref r 就变成了双重引用,不符合题意
         ref mut value => value.push_str(" world!") //此时value就是&str类型,但是这不是引用类型
      }
      println!("{}",r);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行代码

        Finished release [optimized] target(s) in 0.34s
         Running `target/release/world_hello`
    hello, world!
    
    • 1
    • 2
    • 3

    前面我们说了,ref不遵循借用的那一套规则,所以上面的代码是可以运行成功的。

    按照正常思路的话(假设),按道理应该是两个可变引用,是违背借用原则的,会不会是match{}框住了value的作用域导致,其实只有一个可变引用?

    那我们在看一段代码

    
    // 修复错误,尽量少地修改代码
    // 不要移除任何代码行
    fn main() {
      let mut v = String::from("hello,");
      let r = &mut v;
    
      match *r {
         ref mut value => {
          value.push_str(" world!") ;
          println!("{}",r);
        }
      }
      println!("{}",r);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行一下

        Finished release [optimized] target(s) in 0.21s
         Running `target/release/world_hello`
    hello, world!
    hello, world!
    
    • 1
    • 2
    • 3
    • 4

    哈哈,我们已经验证完了

    ref不遵循借用的那一套规则,在match的模式匹配中,ref也可以是不拿所有权的一种引用方法。

  • 相关阅读:
    AI首席架构师10-AICA-蓝翔 《飞桨框架设计与核心技术》
    .Net IDE智能提示汉化(.Net6、AspNetCore)
    在linux(centOS 7)服务器上操作数据库
    药事管理学考试试题及答案
    高阶运维管理,这个工具和思路值得看看
    git 删除大文件记录解决拉取代码超时问题
    dockerfile
    信息系统安全期末复习笔记
    第二十七讲.动态设置相关参数(replication为2和blocksize为10字节)
    客户管理系统在选型过程中遇到的常见问题
  • 原文地址:https://blog.csdn.net/vince1998/article/details/138094618