The `mut` keyword in variable declaration

The problem

Mojo applies themut keyword inconsistently.

  1. x: Int = 0 declares and initializes a mutable variable.
  2. fn foo(self) takes selfimmutably
  3. fn foo(mut self)takes selfmutably.

Proposed Solution

Make x: Int = 0 immutable. Make mut x: Int = 0mutable.

The solution dovetails nicely with the recent elimination of the need for var in variable declarations.

Prior Work

Rust

Drawbacks

Deviates from Python programming where everything is mutable.

Variables initialized over multiple statements would have to be declared with mut.

Acknowledgements

I recognize that mut was removed from variable declarations about 2 years ago (couldn’t find the relevant discussion). I think it’s worth considering if it should be added back in now that var is optional for variable declaration.

Mut serves no real purpose.
Mutable aliasing is checked at the function level; therefore, there is no technical need for mutable aliases with variable declarations.

It’s hard for beginners to learn, and it’s annoying for everyone else to fix the Mut compile errors every time they change the code.

It may be clearer, but you could also use an LSP MUT feature like InlayHints. I don’t really see this as an argument either.

1 Like

You might want to shuffle over to Mojo proposal: renaming `read` to `immut` - #50 by owenhilyard where some discussion in adjacent are is happening.

Mutable aliasing is checked at the function level; therefore, there is no technical need for mutable aliases with variable declarations.

I disagree. Declaring that something isn’t mutable removes the cognitive burden of figuring out whether it is ever mutated when reading the code, and lets the user know that the value will stay the same for the entire function. This is very useful for larger functions which may have to compute some values at runtime because one of the inputs to the computation is only known at runtime.

2 Likes

What exactly is your plan? Would you like to reintroduce ā€œletā€ and rename it ā€œconst,ā€ or introduce ā€œmutā€? Should annotating mutated variables be mandatory or optional? What should the new keyword mean? Would it also apply to struct fields or variables?

I’m surprised you care about reading code. Otherwise, you would always want to introduce features that make writing code by hand faster and result in slightly shorter code. However, introducing these features would also make the code harder to read.

Agree. There are many use cases where you might want something to stay constant which is only known at runtime like the current time, a user input or the return value of any function that can only be calculated at runtime.

There were some discussions on this in the forum and on discord too and there is already a way to achieve a "runtime constantā€œ in Mojo but this has to be implemented separately as Mojo and the stdlib do not provide it out of the box.

fn readonly[o: ImmutOrigin, T: AnyType](ref [o] val: T) -> ref [o] T:
    return val

fn main():
    ref start_time = readonly(get_current_time())
    ...

See The case for explicit `read` variable bindings too.

One easy straight forward solution would be to provide this function in the stdlib and add a description to it (e.g ā€œGet an immutable reference to a runtime value. Prefer comptime for values that are known at compilation timeā€). So anyone asking for a ā€œruntime constantā€ can be pointed to this function. It would avoid code fragmentation too. If this function gets used in many places It would be possible to provide some syntactic sugar for it in Mojo 1.x.