Reputation: 181
#[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);
}
In code, b
is mutably borrowed and c
is immutably borrowed, so this shouldn't compile, but that compiles and runs without any error.
Upvotes: 3
Views: 943
Reputation: 15115
Let's simplify and desugar your example:
fn main() {
let mut a: i32 = 1;
let b: &mut i32 = &mut a;
let c: &i32 = &*b;
println!("{:?},{:?}", b, c);
}
I think the source of confusion is that the prinln!
macro looks like a regular function call but in reality isn't. If we replace the println!
macro with a regular function:
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);
}
Then we get the expected compiler error:
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
So the question now becomes why does println!
work where func
fails. Let's take a closer look at println!
by expanding it:
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),
],
},
));
};
}
The println!
prepends &
to its arguments, so if we revise func
to do the same:
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);
}
It now compiles.
So what have we learned here? If we mutably borrow some data, then immutably borrow from the mutable borrow, and then immutably borrow the mutable borrow and the immutable re-borrow of the mutable borrow, then there's no violation of Rust's ownership rules, since the immutable borrows are all borrowing from the same original mutable borrow which is not being mutated while the immutable borrows are alive.
Upvotes: 2
Reputation: 26167
this shouldn't compile
Why? Yes, b
mutably borrows r
. But c
immutably borrows b
, so now b
is, well, immutably borrowed and you can't mutate it.
If you attempt to assign to any fields of b
, then the compiler will immediately throw an error.
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);
The example is the same as if you remove b
all together. You still wouldn't be able to mutate r
, even though r
is mutable.
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);
Upvotes: 3