前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一起学Rust-引用 · 借用

一起学Rust-引用 · 借用

作者头像
江湖安得便相忘
发布2019-10-08 15:26:08
1K0
发布2019-10-08 15:26:08
举报

BGM警告 前方高能

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

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

一、引用

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

代码语言:javascript
复制
let is_a_reference = &5;  // 对5的引用; &i32类型
let ref other_reference = 5;  //对于5 的引用;&i32类型

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

可变引用定义:

代码语言:javascript
复制
let is_a_reference = &mut 5;  // 对5的引用; &i32类型
let ref mut other_reference = 5;  //对于5 的引用; &i32类型

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

代码语言:javascript
复制
*is_a_reference = 25;
*other_reference = 10;

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

代码语言:javascript
复制
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;

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

代码语言:javascript
复制
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);
}

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

代码语言:javascript
复制
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);
代码语言:javascript
复制
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); //第一个可变引用在此处使用
代码语言:javascript
复制
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);

下方代码是可以编译的:

代码语言:javascript
复制
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);

如下代码是可以编译的。

代码语言:javascript
复制
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);

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

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

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

二、借用

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

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

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

代码语言:javascript
复制
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);
}

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

代码语言:javascript
复制
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);
}

也可以这样用:

代码语言:javascript
复制
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);  // 这里的打印实际就是自动解引用。
}

以上是不可变借用。

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

代码语言:javascript
复制
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);
}

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

代码语言:javascript
复制
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获取解构内成员的不可变引用和可变引用:

代码语言:javascript
复制
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寸大彩电,不过电视只归老大所有,其余三兄弟由于没有遥控器,只有乖乖看的份,不能随意换节目。

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

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

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

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 可回收BUG 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档