Making the `mut` keyword less misleading

Precisely what is it that you consider misleading about mut?

I've read through your links and all this discussion and I still don't know. There's an expressed desire to make it not misleading, but ... what about it do you consider misleading?

  • Your first link: is about interior mutability. Which is interesting, but the short answer is that the person there wants a concept of 'mutable' that involves non-local reasoning. An entirely reasonable desire, not unlike wanting to know what can panic. But a complicated desire that, from what I can see, would involve accessory, 'usually correct' reasoning.

    • It isn't an issue with the mut keyword which lets you know whether the handle it is attached to allows direct mutation. It's an issue with understanding global vs local contracts and what guarantees are transitive (so to speak).
  • Next link is someone who thinks let mut is a lint that doesn't need to be enforced.

    • Not really related to any 'confusion' regarding mut and doesn't address the earlier link at all.
  • Third link is about expressions vs patterns using &var_expression to declare vs &var_pattern to destructure.

    • Again, not related to the issues of either of the previous links. (Does suggest that different IDE highlighting for patterns vs expressions might be nice though - e.g. a different background color.)
  • You mention that let mut "looks like" & mut...

    • I mean, no? For some value of "beginner" everything looks similar, but I'd be surprised if that's an actual sticking point of note. Also, again, seems unrelated to anything else.
  • There's another issue that comes up that some people would prefer immutable vs mutable be framed as shared vs exclusive.

    • The idea is that those two frames are joined in rust, as default. mutable vs immutable seems simplest and most locally phrased, which gels with general Rust theme (making reasoning local via contract enforcement). But in any case they're joint concepts, so no obvious gain from swapping focus from one to the other.

TLDR: I have no idea what part of mut you find confusing and thus am a bit confused about this whole thread. Or, rather, what would you consider non-confusing? I'm all for more ways to clarify Rust, but this just seems like a hodgepodge of unrelated things that sometimes use a basic part of syntax.

1 Like

@trentj previously made the same points I raised here, though with different suggestions. If you still don't get where I'm coming from, read it─it's not that long compared to the rest of the containing thread, which is way too long.

As a user, I’ve never really thought about the misleading nature of the mut keyword. Everything has been purely muscle memory for me. Such a fundamental change, in my opinion, would make more sense to implement in Rust 2 (if there is one).

I kind of like the idea of a world where mut was a different keyword, even if the most popular suggestion uniq seems pretty frightful (personally I think own reads quite nicely) - but it seems inherently pretty absurd to try and change half the however many millions of lines of Rust out there because the keyword doesn't quite have the right vibe.

So long as docs, introductory material, answers on URLO etc., clear up for a new user at some point that it technically has more to do with exclusive access, which just happens to largely in effect be that it is mutable; any questions about interior mutability and the like should pretty naturally fall into place.

I can never remember which way around &'a mut is, so allowing both and formatting to &mut 'a now we have rustfmt editions seems like a neat small improvement?

4 Likes

It's easier to think: &mut T is mutable and therefore it's exclusive. Rust has a philosophy that "mutable => exclusive", and so the natural interpretation is &mut means "it's mutable", which also implies it's exclusive because of Rust's philosophy.

If you think that way, there is no inconsistency.

It's a bit unfortunate that there is no other way to make an exclusive reference than by making it mutable, but that's a different issue. That feature doesn't exist, but it could logically exist. Maybe some day it will be added, it would make the distinction clearer.

5 Likes

This is, IMO, a key point. I don't think the canonical materials (e.g. the book) do an adequate job yet. And there should at least be a heads-up from the beginning, not just "eventually". E.g. learning about interior mutability after 20-some chapters of "immutable references" was quite confounding and ultimately time wasting, in my experience.

2 Likes

The easiest way I have found to think about interior mutability is that a Cell<T> is kind of like &mut T but with optimized memory layout. The Cell is logically separate from the T, so you can change the T without "changing" the Cell itself, just like you can change a pointee without changing the pointer. This makes it consistent with mut meaning "mutable".

I think this misses the point. Interior mutability is really about shared mutability (or maybe "interior exclusivity"). As shown in my example above, &mut T can't do that. The superpower of UnsafeCell is that it allows library code to forge exclusivity out of nowhere (in the case of Cell, by being !Sync).

I agree that Cell can do things that &mut T can't and that what you describe is its superpower. What I was trying to say is that it can be seen as consistent with the mut=mutable story with the right interpretion.

I agree there is a different way to interpret &mut (=exclusive) that is inconsistent with mut local variables, but I see no need to assume that interpretation.

This is probably the core of your argument. You think
mutable IMPLIES exclusive
As in the logical operator. This is factually correct, but I hold your correct mindset doesn't necessarily match a Rust learner's first intuition, which is more likely to be
mutable == exclusive
because IMPLIES is not a common logical relation, and because tracking these two as distinct variables would require twice the mental energy.

This can be seen in our familiar motto "shared XOR mutable". Personally I find it quite catchier compared to "shared NAND mutable".

All in all, I'd rather let be not exclusive so that we all can keep mutable == exclusive facade and make Rust more usable to those who cannot keep the distinction in their head.

That's exactly right.

That's a good point. I'll start insisting it should be "nand" whenever somebody says this :slight_smile:

Do you mean that you'd rather let always be mutable? Let's say we do that, and perhaps rename &mut to &ex.

This would mean that this n would no longer be immutable:

let n = v.len();

I believe that programmers would then want some concept of const introduced. Just like they did in C, in Java (final), in Javascript. In all these languages the concept of constness was added at some point.

So you'd still end up with two different concepts.

This is a crucial point. Suppose that &uniq T is a reference type that is exclusive just like &mut T, but without the ability to mutate.

Then, mut in Rust is explained as:

  • &mut T is a more ergonomic &uniq UnsafeCell<T>
  • let x: T gives the ability to borrow &uniq T
  • let mut x: T gives the stronger ability to borrow &mut T

The existing ability of let r: &mut T to mutate the T would just be a natural consequence of &uniq &mut T derefing to &mut T.

2 Likes

I've never suggested that, right? I meant I'd rather have let presumed as shared, so that "shared XOR mutable" could ring true all the way down to here.

Surely you can, but I expect it's impossible to change the popular usage of the term at this point. Or if it's possible, perhaps &uniq is possible too.

That would mean these wouldn't work:

let s = String::from("hello");
let s2 = s; // ERROR: `s` is shared, can't move out of it
drop(s); // ERROR: can't drop a shared object
f(s); // ERROR: can't move out of a shared object
2 Likes

Ideally, something like this would be implemented:

More realistically, we just continue to allow moves unless they're behind a physical & reference.

The explanation chosen is mostly orthogonal to the explanation not being present or only being present way too late in the learning material.[1] Most material either doesn't cover it, or covers it without acknowledging it's a contradiction to the preceding 80+% of the material (and in either case doesn't give you a heads-up from the start that you're being given an incomplete picture for the sake of a more graspable presentation).


  1. Though poor or incorrect explanations are a pain point in Rust learning material too... ↩︎