Mutable reference to self when only shared reference is possible in std?

Trying to understand this from std::io::Read:

impl Read for &[u8] {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        // ... snip ...
    }

It looks like it is only implemented for immutable slices, so how is calling read ever allowed? Self is &[u8] so how would you be allowed to make &mut self? The documentation says that it changes the slice to be narrowed to refer to the remaining bytes, so I can see why it would need to change the slice itself, I just don't know how to reconcile how the mutability works here. As far as I can tell there are two things to worry about mutability of, the underlying bytes the slice refers to, and the position and length of the slice. How syntactically do I write the cross product of (slice field mutability) x (underlying byte mutability) ?

edit: ... or since Self is a reference type then &mut self is &mut &[u8]?

Indeed. &mut self is a shorthand for self: &mut Self. When Self is &[u8], &mut Self is of course &mut &[u8] - and thus &mut self is also &mut &[u8].

2 Likes

Yes. Because we're in the context of

impl Read for &[u8]
//            ^^^^^

We have Self = &[u8] (for any anonymous lifetime of the reference). Thus in the method, the type of &mut self is &mut Self is &mut &[u8] (for any two anonymous lifetimes which are well-formed).


In order for &mut self to be a &mut [u8], we would instead need Self = [u8], i.e.

impl Read for [u8] { /* ... */ }

which is not what is implemented. (However it is completely valid to implement traits on slices more generally.)

1 Like