关于变量的可变引用的问题

问题

先贴代码

fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}",s1);
    println!("{}",s2);
}

上面代码报错

error[E0502]: cannot borrow `s1` as immutable because it is also borrowed as mutable
 --> test.rs:4:19
  |
3 |     let s2 = &mut s1;
  |              ------- mutable borrow occurs here
4 |     println!("{}",s1);
  |                   ^^ immutable borrow occurs here
5 |     println!("{}",s2);
  |                   -- mutable borrow later used here

然后代码改成下面这样就可以运行了

fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}",s2);
    println!("{}",s1);
}

猜想

1.在s2引用s1之后会获取值的使用权,s1会失去值的使用权;
2.在编译阶段编译器分析使用权归哪个变量;
3.如问题代码:在s2释放使用权之前访问s1,所以报错;
4.如更正代码:编译器在访问s1时判断之后没有再访问s2,就把s2的使用权释放,s1获得使用权,所以不报错;
5.可变引用可以等价于所有权的两次移动。

最后,请各位指正我的问题,谢谢!

最佳答案

对版主做一下补充,在一开始的版本中第二个代码也会报错,后来的某一版本才合理化(具体版本忘记了)。

fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}",s1);// 因为S2没有释放(drop),此处默认使用的s1的不可变引用,违反了可变引用和不可变引用同时存在的约定 
    println!("{}",s2);
}
下面看看第二个
fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}",s2);
    // 后面没有s2的使用,此时s2已经被drop掉了,
    println!("{}",s1);// 只有s1的不可变引用,此时已经没有s1的可变引用了
}

可变引用(借用)会发生所有权转移,这个可变应用drop后,所有权还是会回到原来的变量上去。

官方手册上的规则

  • At any given time, you can have either one mutable reference or any number of immutable references. (在同一个生命周期内,只能有一个可变引用和多个不可变引用)
  • References must always be valid (保持引用的有效)
3年前 评论
讨论数量: 3

我是 rust 小白,所以以下的解释很可能略显啰嗦,请多多见谅。所有权可以被转移和被借用。借用指针的使用可以用 & 或者&mut 表示。& 表示可读借用, &mut 表示可读写借用,当然也可以将其称为可变引用。引用也能说明一件事情,引用对它指向的内存是没有所有权的。(所以楼主的猜想不对,楼主的猜想的思路是基于所有权转移,而可变引用是所有权借用的概念。转移-move和借用-borrow是两回事。)

小白回顾知识结束!

说下楼主贴的报错的代码,报错信息已经指出了问题,因为 let s2 = &mut s1; 之后打印 s1,编译器会认为打印 s1 之前,存在 s1 被修改的风险,代码改成以下的顺序编译就是 ok 的。

fn main() {
    let mut s1 = String::from("hello");

    println!("{}", s1);

    let s2 = &mut s1;

    println!("{}", s2);
}

再解释下楼主编译 ok 的代码。

fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}",s2);
    println!("{}",s1);
}

s1 打印之后 main 函数结束,编译器认为在 s1 打印之后,不存在 s2 被调用的风险,即不存在 s1 被修改的风险,所以编译是 ok 的。报错信息mutable borrow later used here,说的特别明白,因为可变引用在不可变引用之后被使用了,所以报错了。 验证一下

fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}", s2);
    println!("{}", s1);
    let mut s3 = s2;
}

该段代码会报同样的错。

3年前 评论

doc.rust-lang.org/book/ch04-02-ref...

官方的The Book写的很好,值得反复阅读! file

3年前 评论

对版主做一下补充,在一开始的版本中第二个代码也会报错,后来的某一版本才合理化(具体版本忘记了)。

fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}",s1);// 因为S2没有释放(drop),此处默认使用的s1的不可变引用,违反了可变引用和不可变引用同时存在的约定 
    println!("{}",s2);
}
下面看看第二个
fn main() {
    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    println!("{}",s2);
    // 后面没有s2的使用,此时s2已经被drop掉了,
    println!("{}",s1);// 只有s1的不可变引用,此时已经没有s1的可变引用了
}

可变引用(借用)会发生所有权转移,这个可变应用drop后,所有权还是会回到原来的变量上去。

官方手册上的规则

  • At any given time, you can have either one mutable reference or any number of immutable references. (在同一个生命周期内,只能有一个可变引用和多个不可变引用)
  • References must always be valid (保持引用的有效)
3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!