Why chaning `poll_read(self: ...)` to `poll_read(mut self: ...)` does not change the signature of the trait's function

I was implementing this:

impl AsyncRead for SmolSocket {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>,
    ) -> Poll<std::io::Result<()>> {
        self.member = something

when I got an error that self is not mutable so I cannot do this modification. Adding mut self like this:

fn poll_read(
    mut self: Pin<&mut Self>, 

as suggested by the compiler, worked.

Why I can do this modification? Wouldn't the signature of poll_read be different from the one defined in the trait?

Also, why I get the error in the first place? self is Pin<&mut self> so I should be able to modify the contents of the pinned thing.

The mut that you've added is not part of the function's signature, it's just a local variable binding mode. You're taking ownership of self in that method, so you are free to mutate it or not, because nothing else can see it. Rust just doesn't allow mutation by default, so you have to declare a mutable binding if you want to mutate.

In effect, what you've written is equivalent to this:

fn poll_read(
    self: Pin<&mut Self>,
    cx: &mut Context<'_>,
    buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
    let mut s = self;
    s.member = something
    ...
}
1 Like

indeed, but why isn't the mut inside of Pin enough for me to mutate the internal state of Self?

Mutability in Rust is a property of variable bindings, not types. You have an &mut reference, but the mut here is a bit of a misnomer; it means you have a unique reference to something. In order to mutate the value of a variable, you must have a mut binding, and in order to mutate a value on the other side of a reference, you must also have an &mut reference (or Box, or a few other constructs.) In other words, the mut you have is only telling you the type of the value, and you still need a mut binding to access it with mutable operations.

I believe part of the difference between what you are doing and what &mut Self would do is related to the Pin wrapper. If you had used &mut self and wanted to call mutating methods on it, the compiler would do a 're-borrow', implicitly creating a new &mut reference to pass into the method. It looks like the Pin is getting in the way of this, but I don't really know how Pin is supposed to interact with implicit DerefMut and implicit re-borrowing.

1 Like

That's not really specific to Pin. Consider the following code:

use std::ops::{Deref, DerefMut};

#[derive(Debug)]
struct Container<T>(T);

impl<T> Deref for Container<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> DerefMut for Container<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

fn main() {
    let mut inner = String::from("hello ");
    let wrapper: Container<&mut String> = Container(&mut inner);
    wrapper.push_str("world");
    println!("{:?}", wrapper);
}

Playground
It fails with the following error, very similar to the one OP has likely run into:

error[E0596]: cannot borrow `wrapper` as mutable, as it is not declared as mutable
  --> src/main.rs:22:5
   |
21 |     let wrapper: Container<&mut String> = Container(&mut inner);
   |         ------- help: consider changing this to be mutable: `mut wrapper`
22 |     wrapper.push_str("world");
   |     ^^^^^^^ cannot borrow as mutable

The reason is the desugaring of dereference operation. When we call wrapper.push_str, it's essentially transformed into the following:

{
    let tmp = DerefMut::deref_mut(&mut wrapper);
    tmp.push_str("world");
}

Here, we explicitly create &mut wrapper, since that's what DerefMut::deref_mut needs. But we can't create a &mut reference to the binding which is not mutable itself. If we declare it as let mut wrapper, the code compiles and prints Container("hello world"), as expected.

3 Likes

Rust conflates two things with the mut keyword before bindings (variable names).

  1. Whether you can re-assign it. For variables representing function arguments, it doesn't matter what you do with the variables (not values behind the variables) in the function body — this can't be observed outside of the function.
fn intref(mut int: &i32) {
   int = &0; // nobody can see the effect of this, so nobody cares
}
  1. Whether you can mutate directly by taking a mutable reference to it. When you own an object, you can freely mutate it anyway. Conceptually, poll_read passes ownership of the self object (it's the Pin type, not a bare reference).
let s = String::new(); 
let mut s = s; // we own s, so we can make it mutable whenever we want
s.push_str("hello");

The second case is a very weak lint only, and you can "cheat" it in a few ways, e.g.:

let s = String::new();
{s}.push_str("wat"); // this uses result of `{}` to mutate it, not `s`

but this doesn't matter, because you're the exclusive owner of the object, so you can do whatever you want with it.