Skip to content

Instantly share code, notes, and snippets.

@shifujun
Created June 12, 2022 09:52
Show Gist options
  • Save shifujun/c38fecda685b983c147f827793576c2d to your computer and use it in GitHub Desktop.
Save shifujun/c38fecda685b983c147f827793576c2d to your computer and use it in GitHub Desktop.
A brain-burning question about 'mut' keyword in learning Rust

When I learn Rust's mut keyword and references types, everything seemed logical. Basicly without mut decleared, any member of a struct cannot change.

Until I read to this: https://doc.rust-lang.org/rust-by-example/fn/closures/capture.html

    let mut count = 0;
    // A closure to increment `count` could take either `&mut count` or `count`
    // but `&mut count` is less restrictive so it takes that. Immediately
    // borrows `count`.
    //
    // A `mut` is required on `inc` because a `&mut` is stored inside. Thus,
    // calling the closure mutates the closure which requires a `mut`.
    let mut inc = || {
        count += 1;
        println!("`count`: {}", count);
    };

    // Call the closure using a mutable borrow.
    inc();

The inc closure get a &mut count, and use it to update count. I think while updating count, the &mut count is not modified. So inc should be immutable.

The explanation doesn't make sence, I don't think "calling the closure mutates the closure". I cannot figure out what changed of the int closure.

After Google, I read: https://internals.rust-lang.org/t/closure-value-moved-why-mut-needed/11082/16 https://www.reddit.com/r/rust/comments/8gbp22/explanation_struct_with_mutable_reference_field/

Those discussion help a little, but I finally understand by myself.

A mut value can only be access by one vairable at a time. Either a "owner" or a "mut reference". If we pass a &mut to another struct, we should also make sure when using that &mut field, it also can only be access by one vairable at a time.

So with a shared reference(&) to this another struct access to its &mut field is not allowed. Because shared reference can be mutiple.

That leave us two options: with "owner" vairable or "&mut".

With "owner" vairable, it looks reasonable. Like this:

    let foo = Foo {
        hold_mut_ref: &mut value,
    };

    *(foo.hold_mut_ref) += 1;

foo is immutable, no mut keyword needed.

With "&mut", it looks rediculous. Like this:

    let mut value = 0;

    let mut foo = Foo {
        hold_mut_ref: &mut value,
    };

    let ref_foo = &mut foo;
    *ref_foo.hold_mut_ref += 1;

foo itself doesn't changed, but it need to be mark mut.

The only reason for foo need mut is to make sure foo cannot be borrowed twice, which may lead to hold_mut_ref be borrowed twice, which lead to multiple mutable borrowing.

The key to this question is to accept that 'mut' keyword is not only for allowing mutate the value being marked, but also for making sure only one reference can access to the value at a time.

Back to inc closure, inc closure captures a &mut. If we can call inc with "owner" variable, inc doesn't need to be mut. But calling a closure is through reference by compiler design. So for same reason as Foo above, calling closure is calling FnMut trait, which need a &mut self, for locking the &mut captured by it.

Maybe mut should rename to lock. Because immutable data is thread safe without any lock. The only reason we need a lock is to mutate data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment