一起学Rust-引用 · 借用

BGM警告 前方高能

接续上一期的所有权的学习,所有权的内容中强调的是变量是资源的所有者,拥有对资源的控制权(例如移动,释放),但并不是所有的变量都拥有所指向的资源,那就是引用(Reference)

这次来了解Rust中的引用该咋用和需要注意的规则。

一、引用

定义不可变引用(关注定义,不关注代码是否有意义)下列两个定义是相同的:

let is_a_reference = &5;  // 对5的引用; &i32类型
let ref other_reference = 5;  //对于5 的引用;&i32类型

& 符号在赋值时要用在值的前面,ref用在变量的前面。

可变引用定义:

let is_a_reference = &mut 5;  // 对5的引用; &i32类型
let ref mut other_reference = 5;  //对于5 的引用; &i32类型

代表的是对引用的数据的修改权,修改时则需要 解引用(dereference)

*is_a_reference = 25;
*other_reference = 10;

引用规则:同一资源可以存在多个不可变引用

let num_saved_in_stack = 5;
let immutable_ref1 = &num_saved_in_stack;
let immutable_ref2 = &num_saved_in_stack;
let immutable_ref3 = &num_saved_in_stack;

引用规则:同一资源存在引用时,该资源在引用释放前或使用之前不可被移动或释放。

fn move_value(_s: String) {
    println!("s will drop in the fn");
}

fn main() {
    let ownership_value = String::from("test");
    let ref str_ref = ownership_value;
    println!("{}", str_ref);

    //所有权移动到方法内。
    move_value(ownership_value);
    //? 注释掉下方行则会消除错误。
    println!("{}", str_ref);
}

引用规则:同一资源一次只能存在一个可变引用,同一作用域内不可存在不可变引用。

let mut ownership_value = String::from("test");
//编译不通过
let ref mut str_ref1 = ownership_value; //第一个可变引用
let ref mut str_ref2 = ownership_value; //第二个可变引用
// 编译失败 同时存在了两个可变引用
println!("{}", str_ref1);
println!("{}", str_ref2);
let mut ownership_value = String::from("test");
let ref mut str_ref1 = ownership_value; //第一个可变引用

{
    let ref mut str_ref2 = ownership_value;//第二个可变引用
    println!("{}", str_ref2);
}
//编译失败,引用使用前引用第二次。
//? 注释掉下方行则会消除错误。
println!("{}", str_ref1); //第一个可变引用在此处使用
let mut ownership_value = String::from("test");
let ref mut str_ref1 = ownership_value; //第一个可变引用
let ref str_ref12 = ownership_value;

//编译失败,同一作用域内存在可变和不可变引用。
println!("{}", str_ref1);
println!("{}", str_ref12);

下方代码是可以编译的:

let mut ownership_value = String::from("test");

{
    let ref mut str_ref2 = ownership_value;
    println!("{}", str_ref2);
}
//此时第一个可变借用已经释放,
let ref mut str_ref1 = ownership_value;
println!("{}", str_ref1);

如下代码是可以编译的。

let mut ownership_value = String::from("test");
let ref mut str_ref1 = ownership_value;
println!("{}", str_ref1);

let ref mut str_ref2 = ownership_value;
println!("{}", str_ref2);

总结这一规则:在引用声明和引用使用的中间不能包含对该资源对第二次引用,规则中的“一次”也就是指这个意思,一次引用可以考虑为,从声明开始到最后一次使用结束。

引用规则:资源存在使用的引用时,在当前作用域中这一资源是不可被修改的。称之为“冻结”。其实与上一个规则有些相似。

let mut a = Box::new(5);
let b = &a;
a = Box::new(6);
// 以下编译时会发生错误。
println!("{}", b);
println!("{}", a);

二、借用

借用是与引用密不可分的,当把引用用作方法的参数,则称之为借用(borrow)

Rust的编译器内存在一个借用检查器,检查的就是上一小节的引用规则。

借用使用场景:当方法不需要获取输入参数的所有权,则需要使用借用。如下例子中borrow_fn并不需要获取n的所有权,仅仅使用值进行判断。

fn borrow_fn(n: Box<i32>) {
    //解Box引用
    if *n > 2 {
        println!("gather than 2");
    } else {
        println!("less than or equal 2")
    }
}

fn main() {
    let num = Box::new(5);
    // ? 发生所有权转移;
    borrow_fn(num);
    // ?注释掉可消除错误,err:使用了已经移动的变量。
    println!("{}", num);
}

所以需要使用借用,可以这样用:

fn borrow_fn(n: &Box<i32>) {
    //解Box引用,需要解两次引用
    if **n > 2 {
        println!("gather than 2");
    } else {
        println!("less than or equal 2")
    }
}

fn main() {
    let num = Box::new(5);
    // ? 发生借用;
    borrow_fn(&num);
    // 借用不拥有原始资源,无权释放,后面可以继续使用。
    println!("{}", num);
}

也可以这样用:

fn borrow_fn(n: &i32) {
    //解引用
    if *n > 2 {
        println!("gather than 2");
    } else {
        println!("less than or equal 2")
    }
}

fn main() {
    let num = Box::new(5);
    // ? 发生借用,Box的自动解引用起作用
    borrow_fn(&num);
    println!("{}", num);  // 这里的打印实际就是自动解引用。
}

以上是不可变借用。

可变借用(同样遵守引用规则):

fn mut_borrow_value(n: &mut i32) {
    *n = *n * 2;
}

fn main() {
    let mut num = Box::new(5);
    // ? 发生可变借用;原始值需要是可变的。
    mut_borrow_value(&mut num);
    println!("{}", num);
}

借用时,可变引用可以作为不可变引用参数,反之则不行:

fn mut_borrow_value(n: &i32) {
    if *n > 2 {
        println!("gather than 2");
    } else {
        println!("less than or equal 2")
    }
}

fn main() {
    let mut num = Box::new(5);
    // ? 发生可变借用;原始值需要是可变的。
    mut_borrow_value(&mut num);
    println!("{}", num);
}

三、解引用与解构

解引用需要使用 * ,上面的例子中也已经展示过了。

而需要获取解构的引用时则情况不太一样,可以使用ref获取解构内成员的不可变引用和可变引用:

struct Rect {
    w: i32,
    h: i32,
}

fn main() {
    let r = Rect{w:1, h:1};
    let Rect{w: ref ww, h} = r;

    println!("{}, {}", ww, h);
}

传说

有一个瞎编古老的传说:很久很久以前,唐老大与唐老二、唐老三、唐老四被江湖人称唐氏四兄弟。传说他们有一台32寸大彩电,不过电视只归老大所有,其余三兄弟由于没有遥控器,只有乖乖看的份,不能随意换节目。

唐家家规很严,对电视节目的播放非常严格,管家每次都会监视他们:

  • 只要有兄弟在看电视,就不准换节目,包括老大在内;
  • 遥控器只有一个,所以只能由一个人来换节目;
  • 要想换节目就要让所有兄弟出去,不许看电视,调好节目再回来看;

正是唐家家规的这么严格,造就了唐家人铁一样的坚毅性格,而且从来没有因为抢遥控器看节目发生过争执,后来在动漫江湖中有了很大的成就。

你学会瞎编引用借用了吗?

本文分享自微信公众号 - 可回收BUG(way-of-full-stack)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券