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.