今天来学习rust中的ref关键字。

由一个例子引出ref关键字的使用

先看下面的例子。

例1:

 1
 2#[derive(Debug)]
 3enum HttpMethod {
 4    Get(Get),
 5    Post(Post),
 6}
 7
 8#[derive(Debug)]
 9struct Get {
10    url: String,
11}
12
13#[derive(Debug)]
14struct Post {
15    url: String,
16    body: Vec<u8>,
17}
18 
19 fn main() {
20    let method = HttpMethod::Get(Get{
21        url: String::from("https://google.com"),
22    });
23
24    match  method  {
25        HttpMethod::Get(get) => println!("send get reuqest: {:?}", get),
26        HttpMethod::Post(post) => println!("send post reuqest: {:?}", post),
27    }
28
29    println!("{:?}", method); // 编译错误: borrow of partially moved value: `method`
30     
31 }

上面例1中在使用match表达式进行模式匹配时,在执行第25行或26行时method会发生部分移动(partially moved),根据rust所有权规则中的Move语义,method将失去所有权,因此在第29行无法再使用method变量,报了编译错误。

假设这里的需求是,希望在match表达式之后继续使用method变量。 为了在第29行继续使用method变量,就要想办法避免在match表达式模式匹配中发生部分移动。

根据前面已经学习的知识,你可能会想到使用Borrow语义,在match表达式中使用引用,于是做了下面的修改。

例2:

 1 fn main() {
 2    let method = HttpMethod::Get(Get{
 3        url: String::from("https://google.com"),
 4    });
 5
 6    match  &method  {
 7        HttpMethod::Get(get) => println!("send get reuqest: {:?}", get),
 8        HttpMethod::Post(post) => println!("send post reuqest: {:?}", post),
 9    }
10
11    println!("{:?}", method);
12     
13 }

例2的修改将match表达式的中method换成了引用&method,这样确实能够编译通过。

今天要学的是另一种实现方式,即使用ref关键字在模式匹配中通过引用进行绑定,使用ref关键字,可以将例1修改如下。

例3:

 1 fn main() {
 2    let method = HttpMethod::Get(Get{
 3        url: String::from("https://google.com"),
 4    });
 5
 6    match method  {
 7        HttpMethod::Get(ref get) => println!("send get reuqest: {:?}", get),
 8        HttpMethod::Post(ref post) => println!("send post reuqest: {:?}", post),
 9    }
10
11    println!("{:?}", method);
12     
13 }

例3的代码也是可以编译通过的。下面来学习一下ref关键字的基本概念。

ref关键字

前面例1的代码无法编译的原因是:

Rust的match表达式默认会消耗掉它所能消耗的所有值这在不希望发生移动和转移所有权的场景下会成为一个问题。

此时可以使用ref关键字标注模式绑定,这样模式绑定时将会使用(Borrow)借用而不是移动(Move)。

那么上面例2和例3的match表达式中&ref有什么区别呢?

  • &表示模式期望是一个对象的引用。即,&是所述模式的一部分。上面例2中所模式期望匹配的是&HttpMethod,而不是HttpMethod
  • ref表示你想要一个对未打包的值的引用,它不被匹配。因此例3中虽然用了ref关键字,但模式期望匹配的还是HttpMethod

参考