Cannot Borrow `self` As Mutable When self is `&mut Self`

Hi all, I'm trying to learn about rust's trait, and I wrote this code:

use std::io;

trait SomeReadExt: io::BufRead {
    fn read_number(&mut self) -> io::Result<i64> {
        let mut buff: [u8; 2] = [0; 2];
        self.read_exact(&mut buff)?;
        Ok(((buff[0] as i64) << 8) | (buff[1] as i64))
    }
}

impl<R: io::BufRead> SomeReadExt for R {}

pub trait SomeRead: io::BufRead {
    fn read_value(&mut self) -> io::Result<Vec<u8>> {
        let n = self.read_number()?;
        let mut buff = vec![0u8; n as usize];
        self.read_exact(&mut buff)?;
        Ok(buff)
    }
}

impl<R: io::BufRead> SomeRead for R {}

But somehow, it got this error

error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
  --> src/test.rs:15:17
   |
15 |         let n = self.read_number()?;
   |                 ^^^^^^^^^^^^^^^^^^ cannot borrow as mutable

You can also reproduce it in this playground: Rust Playground.

I don't understand why self can't be borrow as mutable while self itself is a &mut Self. Is there any explanation for this error? Thank you.

Wow, this is a pretty horrible error. So, what's going on is that it's trying to use the implementation of SomeReadExt for &mut Self instead of Self, for reasons I'll get to in a moment. This implementation exists because BufRead has an implementation for &mut R where R: BufRead + ?Sized, i.e., you can use an arbitrarily nested &mut &mut ... &mut R.

This in turn requires passing a &mut &mut self, but your &mut self itself is not mutable. If you had a non-self parameter you could do

fn read_value(mut this: &mut SomeType) { /* ... */ }
//            ^^^

So one fix could be to do

    fn read_value(&mut self) -> io::Result<Vec<u8>> {
        let mut this = self;
        let n = this.read_number()?;
        let mut buff = vec![0u8; n as usize];
        this.read_exact(&mut buff)?;
        Ok(buff)
    }

But you don't really want to use this nested implementation, you want to use the implementation for Self. The problem is that it does not exist, for subtle reasons.


Above I mentioned

BufRead has an implementation for &mut R where R: BufRead + ?Sized

What's that ?Sized bound? It actually removes an implicit bound that R is Sized -- has a size known at compile time -- is not dynamically sized. This implicit bound is present when you introduce type parameters.

However, it is not present for the implementor of a trait (or as a supertrait bound on a trait declaration). So in this default method body, you don't get to rely on Self: Sized.

Above you have:

impl<R: io::BufRead> SomeReadExt for R {}

The R here does have the implicit Sized bound. So back in your read_value function body, you can't use this implementation for Self, because you can't assume Self: Sized. However, you do know that &mut Self is Sized, and that's covered by the implementation. So that's what the compiler is trying to do. Whew!


Thus, the actual fix is to make this change:

impl<R: io::BufRead + ?Sized> SomeReadExt for R {}
//                    ^^^^^^

Now it doesn't matter if Self is Sized or not, and you can use the implementation of SomeReadExt for Self.

Playground.

7 Likes

Mini-update: The syntax for a mutable &mut self is

    fn read_value(mut self: &mut Self) -> io::Result<Vec<u8>> {

(But it's not really the correct suggestion here, and probably a sign something weird is going on generally.)

Wow, really nice explanation, thank you.

Got it, I think it works now, thanks.

Mentioned this thread in #93078.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.