About references in println!

Hi,

I'm new to Rust and reading The Book now. In the References and Borrowing section:

We call the &T type a ‘reference’, and rather than owning the resource, it borrows ownership.

[...]

fn main() {
    let mut x = 5;
    let y = &mut x;
    *y += 1;
    println!("{}", x);
}

This code gives us this error:

error: cannot borrow `x` as immutable because it is also borrowed as mutable
    println!("{}", x);
                   ^

I don't understand why x is borrowed as immutable, because the second parameter of println! is x, not &x. Can anyone point me in the right direction? Thank you!

Well, if println! can't borrow x, how do you expect it to read the value in order to print it?

It's complaining about borrowing, that's due to println! (and the whole formatting family of macros) implicitly borrowing their arguments because, again, it has to be able to read from those arguments in order to print them.

Why borrow and not move? Because printing a value shouldn't destroy it in the case of things like String.

3 Likes

The thing is that x is borrowed inmutably (by y) and we can't have mutable and inmutable borrows at the same time. One option would be to print y, that I think it would work, or, creating an artifical scope where you borrow x mutably:

let mut x = 0;
{
    let y = &mut x;
    *y +=1;
}// Can't access `y` anymore, so now `x` is no longer borrowed

println!("{}", x);

Just to clarify even more, mutable access in Rust is exclusive. While the mutable reference to x (let y = &mut x;) is in scope, there is no other way to access the value in x than through that mutable reference.

Hey all, is this still relevant on the latest versions of RUST. I canno't reproduce this behaviour...

Latest versions contain new version of borrow checker (called NLL) that can see that y is no longer used after last +=, so it compiles. Previously you needed to introduce {} manually.

You can still repro this issue now if you reorder last two lines (so print is before +=).

You can also get before-NLL behavior by having drop glue:

struct NoNll<T>(pub T);
impl<T> Drop for NoNll<T> { fn drop(&mut self) {} }

fn main ()
{
    let mut x = 5;
    let y = NoNll(&mut x);
    *y.0 += 1;
    println!("{}", x);
}