为什么你可以借用一个可变引用并仍然使用两者?

Why can you borrow a mutable reference and still use both?

#[derive(Debug)]
struct Rect {
    width: u32,
    height: u32,
}

fn main() {
    let mut r = Rect { width: 30, height: 30 };
    let b = &mut r;
    let c: &Rect = b;
    println!("{:?},{:?}", b, c);
}

在代码中,b 是可变借用的,c 是不变借用的,所以这不应该编译,但编译和运行没有任何错误。

this shouldn't compile

为什么?是的,b 可变借用 r。但是 c 不可变地借用了 b,所以现在 b 是不可变地借来的,你不能改变它。

如果您尝试分配给 b 的任何字段,那么编译器将立即抛出错误。

let mut r = Rect { width: 30, height: 30 };
let b = &mut r;
let c: &Rect = b;

// This assignment will fail, as yes, `b` is a
// mutable reference but it is immutably borrowed.
b.width = 40;

println!("{:?},{:?}", b, c);

该示例与您一起删除 b 相同。你仍然无法改变 r,即使 r 是可变的。

let mut r = Rect { width: 30, height: 30 };
let c: &Rect = &r;

// This assignment will fail for the same reason,
// `r` is a mutable reference but it is immutably borrowed.
r.width = 40;

println!("{:?},{:?}", r, c);

让我们简化您的示例并对其进行脱糖处理:

fn main() {
    let mut a: i32 = 1;
    let b: &mut i32 = &mut a;
    let c: &i32 = &*b;
    println!("{:?},{:?}", b, c);
}

我认为混淆的根源是 prinln! 宏看起来像一个常规函数调用,但实际上不是。如果我们用常规函数替换 println! 宏:

fn func(format: &str, b: &mut i32, c: &i32) {}

fn main() {
    let mut a: i32 = 1;
    let b: &mut i32 = &mut a;
    let c: &i32 = &*b;
    func("{:?},{:?}", b, c);
}

然后我们得到预期的编译器错误:

error[E0502]: cannot borrow `*b` as mutable because it is also borrowed as immutable
 --> src/main.rs:7:5
  |
6 |     let c: &i32 = &*b;
  |                   --- immutable borrow occurs here
7 |     func("{:?},{:?}", b, c);
  |     ----^^^^^^^^^^^^^^^^^^^
  |     |
  |     mutable borrow occurs here
  |     immutable borrow later used by call

所以现在的问题变成了为什么 println!func 失败的地方工作。 println! 展开来仔细看看:

fn main() {
    let mut a: i32 = 1;
    let b: &mut i32 = &mut a;
    let c: &i32 = &*b;
    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(
            &["", ",", "\n"],
            &match (&b, &c) { // borrows b & c again here
                (arg0, arg1) => [
                    ::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt),
                    ::core::fmt::ArgumentV1::new(arg1, ::core::fmt::Debug::fmt),
                ],
            },
        ));
    };
}

println!& 添加到它的参数前面,所以如果我们修改 func 做同样的事情:

fn func(format: &str, b: &&mut i32, c: &&i32) {}

fn main() {
    let mut a: i32 = 1;
    let b: &mut i32 = &mut a;
    let c: &i32 = &*b;
    func("{:?},{:?}", &b, &c);
}

现在可以编译了。

那么我们在这里学到了什么?如果我们可变地借用一些数据,然后不变地从可变借用处借用,然后不变地借用可变借用和可变借用的不可变再借用,那么就没有违反 Rust 的所有权规则,因为不可变借用都是从当不可变借用还活着时,相同的原始可变借用不会被改变。