rust-lang / book

The Rust Programming Language
https://doc.rust-lang.org/book/
Other
15.03k stars 3.39k forks source link

Confusing section: Wrapping mltiple mutable references in curly braces #3485

Open jish opened 1 year ago

jish commented 1 year ago

Hello, I apologize if this is the incorrect place for book feedback. When looking at the main page and its links, I did not find any other instructions for submitting feedback https://doc.rust-lang.org/book/title-page.html

In Chapter 4 Section 2 https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html about half way down the page is a section that begins with

"As always, we can use curly brackets to create a new scope, allowing for multiple mutable references, just not simultaneous ones:"

    let mut s = String::from("hello");

    {
        let r1 = &mut s;
    } // r1 goes out of scope here, so we can make a new reference with no problems.

    let r2 = &mut s;

I do not understand this suggestion to wrap the first mutable reference in curly braces. I'm attempting to run through some exercises, but I'm having trouble filling in the gaps in this code sample.

Later on down the page it says:

"Note that a reference’s scope starts from where it is introduced and continues through the last time that reference is used."

So if we create a reference and then use it before we create a second reference, the code works fine.

    let mut s = String::from("hello");

    let r1 = &mut s;
    println!("{}", r1);

    let r2 = &mut s;
    println!("{}", r2);

Could you please help me understand how the curly braces from listings/ch04-understanding-ownership/no-listing-11-muts-in-separate-scopes https://github.com/rust-lang/book/blob/2bd5d42c9956369132228da6409f0e68da56c51a/listings/ch04-understanding-ownership/no-listing-11-muts-in-separate-scopes/src/main.rs help with multiple mutable references?

If there is a better place to have this discussion, I apologize and I would be happy to move the topic elsewhere. I just figured that you may appreciate some feedback from a newcomer and a fresh set of eyes. I personally found the curly braces code example to be a bit confusing, and I could use some help understanding it. Maybe it would also help future readers if we could clarify the section in the book about curly braces and mutable references, thanks! 😄

c-git commented 1 year ago

I'm not sure I understood your question. What I understood was you were trying to understand why the curly braces were necessary?

TLDR it used to be necessary but now it is not.

From my understanding, they used to be necessary before support was added for non-lexical lifetimes. And they are not necessary anymore. You can only have 1 mutable reference at a time and because the are in the same lexical scope (some block in the same curtly braces), then the borrow checked used to complain that you are starting a new mutable borrow before you finished with the first one. But now it is able to realize that the first one isn't used anymore and can be released.

jish commented 1 year ago

I'm not sure I understood your question.

I'm sorry, let me try to rephrase it. 😞

As a newcomer to Rust reading through The Rust Programming Language for the first time, what do I need to know about curly braces as they relate to mutable references?

Is some part of the code example missing?

It sounds like maybe this is an old incomplete code example for an old version of Rust. I'm sorry, as I'm new to Rust I don't think I can fully read and digest that RFC until I better understand the language. 😕 Is this feature deprecated? And if so, should old deprecated code that no longer works still be in the book? Or should it be removed?

Or, is there some more detail we can add to that section, in order to clarify the relationship between curly braces and mutable references, so that newcomers can better understand this new and exciting language? 😄

c-git commented 1 year ago

The great part is the old code still works. I think the problem that the braces are trying to solve no longer exists. But more importantly the idea that is trying to be communicated is still valid. You cannot have any other references if you want to have an active mutable reference.

For example the code below gives an error to compile because the mutable reference needs to live beyond the point where the other reference is created.

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

    let r1 = &mut s;

    let r2 = &mut s;

    println!("{r1}");
}

But curly braces do allow you to control the scope of a variable. For example the code below won't compile because by the time the println comes r1 has been dropped.

    let mut s = String::from("hello");

    {
        let r1 = &mut s;
    } // r1 goes out of scope here and can no longer be used

    println!("{r1}");
chriskrycho commented 6 months ago

Sorry you were confused by this, @jish. @c-git’s explanations here are great! Looking at the book, I am not positive what the best approach here is. Although in this very specific example it does work without the extra block, in many cases introducing a block like that for scope is still useful, so it's handy to have an example of it—and the current example isn’t a great motivation, since a lot of things work more easily or naturally since this was originally written! I am going to leave this open as a question for us to think about whether we want to tweak it a bit.