Lack of `mut` in bindings as a deny-by-default lint

I just peeked back into that zulip discussion (I think I took a quick look at the time as-well), and thought about the point that downgrading from owned access to mutable access requires adding a mut. E.g. for calling an FnMut, you'll need to explicitly move (weird syntax) or add mut to the variable, while an FnOnce can be called just like that.

If let mut vs. let is supposed to be distinguished by the guarantee that the latter cannot be mutated between the initialization of the variable and any of its use cases (ignoring interior mutability of course), then I think there is not really any reason against allowing a single, final mutable access of the variable. I mean, the variable does not observably change if it isn't ever used again.

I don't know how hard that's to implement, but I wouldn't mind if something like

fn foo(f: impl FnMut()) {
    f();
}

just worked and you wouldn't need to do

fn foo(mut f: impl FnMut()) {
    f();
}

or perhaps

fn foo(f: impl FnMut()) {
    ({f})();
}

instead.

Note that I'm saying that

fn foo(f: impl FnMut()) {
    f();
    f();
}

should still fail. And also note that

fn foo(f: impl FnMut()) {
    f();
    /* more code, not using `f` */
}

should not move and drop f earlier than usual, f is still only dropped at the end of foo's body.


This could be considered similar to how temporaries can be mutated - effectively - only once. (Because you can only write the expression that creates them into a single place.) Although there's the additional power of allowing any kind of immutable access before the final mutable access.

Of course, taking a reference once is a single mutation in this logic, so

fn main() {
    let x = 42;
    let r = &mut x; // single (final) mutable access of `x`
    *r += 1;
    println!("{r}");
    *r += 1;
    println!("{r}"); 
}

would work just fine; and it's similar to how

fn main() {
    let r = &mut 42;
    *r += 1;
    println!("{r}");
    *r += 1;
    println!("{r}"); 
}

works, too, without a single let mut.


Of course the increased complexity that we'd get from such a change with its inherent increased difficulty of teachability, etc., for a feature that's entirely irrelevant for memory safety purposes (i.e. there's no big benefits from adding this complexity) means that all of the above is maybe a bad idea, I suppose.

10 Likes