Can I require mutability when calling a function?

Sorry, I made two posts here, and I didn't read the thread well before. My apologies.

My third attempt, sorry.

First of all, consuming self and returning a type with a type parameter changed is also used by the state builder pattern. Thus making Pin::enable consume Self can be justified if you want some compile-time safety.


Writing

    fn enable(mut self) -> Pin<mode::Enabled> {

instead of

    fn enable(self) -> Pin<mode::Enabled> {

has the only difference that within the function body, you may obtain a &mut reference to self, e.g. to call methods which take &mut self, or to assign the local self to a new value (of the same type).

Whether you write mut self or self solely depends on what you do in the implementation of the method. It has no effect on the outside interface.

Note that in @semicoleon's example, the disabled variable stores a Pin<mode::Disabled>. This variable is never mutated. Its value is converted into a value of a different type, namely Pin<mode::Enabled>. When you consume a value, you do not mutate it. See also the following example:

fn main() {
    let a = String::from("Hello"); // not `mut`
    let b = String::from(" World");
    let x = [a, b].concat(); // we consume `a` and `b` to create a new `String`
    println!("{x}");

    let mut c = String::from("Hello"); // `mut`
    c.push_str(" World");
    println!("{c}");
}

(Playground)

(Some more examples in this other Playground.)

So in this code:

fn main() {
    let disabled = Pin::new();
    let mut enabled = disabled.enable();

    enabled.write(true);
}

the variable disabled will not be mutated. It will be consumed to create a new enabled value (of a different type).


Additional note, this is a bit unrelated to what you have asked:

You might even consider to remove the mut for the enabled, and to make write work on &self instead of &mut self (depending on your needs). This is because mut in Rust isn't only linked to mutability but also to exclusive access. A mutable reference is also always an exclusive one. This means you cannot access the hardware pin from two threads, for example, or from within multiple event or interrupt handlers.

This is why it often can make sense to make state-altering methods working on &self. This concept is called Interior Mutability. If a type implements the Sync marker trait, it guarantees that it's okay to send shared references to different threads. This way, you can modify the state from several threads in a synchronized fashion.

For types which do not implement Sync or which require &mut self on their methods, you can wrap the type in a Mutex.

But if your struct already performs atomic operations on the hardware, it might be a disadvantageous to demand a &mut self just for the sake of semantics. That is because if you later want to use the pin synchronized from several places, you'll have to wrap it again in a Mutex (or RefCell in the single-threaded cases).

The problem here is that mut really has two meanings, which are partly overlapping:

  • exclusive access
  • mutability / changing state

It's used for both purposes in Rust.

2 Likes