We have the concept of “exclusive” (or mutable) references &mut T in Rust.
The only way to create value of that type in safe Rust is to (at some point) have a let mut foo: T = .. and then do &mut foo somewhere else.
Owning a value and not marking that mut in let mut foo = make_owned_value(); does not mean that it is not possible to mutate it. All you have to do is to move foo and now you are allowed to mutate the moved foo.
You can simply do this with:
fn main() {
struct Foo;
fn make_owned_value() -> Foo { Foo }
fn identity<T>(x: T) -> T { x }
// not marked as mut:
let foo = make_owned_value();
// we got a 'mut' variable from a non-mut one by moving:
let mut foo2 = identity(foo);
// other stuff that mutates foo2 and uses foo2.
}
As an aside, I think therefore that it is morally right to call &mut an exclusive reference as @SimonSapin puts it, and not a mutable reference.
During a discussion on #rust with @withoutboats, @nox, et al. I had the idea of introducing freeze bindings which are truly immutable in the sense that once you’ve frozen an owned value, you can’t mutate it even if you move ownership. An example in code would be:
struct Foo(usize);
fn identity<T>(x: T) -> T { x }
fn id_freeze(
// We acknowledge that `x` is frozen.
// Or perhaps: `x: freeze Foo`
freeze x: Foo)
// The type system must be able to trace frozen values,
// so it becomes part of the type.
-> freeze Foo
{
x
}
// We can define `freeze`.
// This accepts non-frozen and frozen values and freezes them.
// This suggests that `T <: freeze T`, i.e: a `T` is a subtype of `frozen T`.
fn freeze<T>(x: T) -> freeze T { x }
let freeze foo = Foo(0);
let freeze foo2 = id_freeze(foo); // Legal.
let foo2_shared = &foo2; // Legal
let foo2_excl = &mut foo2; // Illegal
foo2.use_mut_receiver(); // Illegal
identity(foo2); // Illegal.
let mut foo3 = id_freeze(foo); // Illegal.
let foo = Foo(0);
let freeze foo2 = id_freeze(foo); // Legal.
// Frozen values are infectious inwards to fields.
// Let's pretend that Foo does not contain a copy type.
let freeze foo = Foo(0);
let mut inner = foo.0; // Illegal, assuming that typeof(foo.0) is not Copy.
let inner = foo.0 // Illegal, assuming that typeof(foo.0) is not Copy.
let freeze inner = foo.0; // Legal
// Legal since it flows from a &T shared reference:
let mut inner = foo.0.clone();
These are just some vague ideas at this point and may be wholly useless or very not nice to work with, but I thought I’d share the idea with y’all. @withoutboats also thought them related to the Pin story.