David
David

Reputation: 1830

How to pass immutable slice to fn that takes an &mut impl Read?

I have an fn that looks something like this:

use std::io::Read;

fn read_something(file: &mut impl Read) {
    let _ = file.read(&mut [0; 8]);
}

When I pass a slice of a Vec into it like that:

fn main() {
    let vector = vec![1, 2, 3, 4];
    read_something(&mut &vector[..]);
}

it works fine. But when I save the slice into a variable first it does not compile:

fn main() {
    let vector = vec![1, 2, 3, 4];
    let slice = &vector[..];
    read_something(&mut slice);
}

The compiler tells me, that I

cannot borrow as mutable

Playground

How do these two cases differ? Why does the first example work even though the vector is not declared as mutable either? How can I pass a &[u8] slice into that fn, that eats a &mut impl Read?

Upvotes: 3

Views: 239

Answers (1)

user4815162342
user4815162342

Reputation: 155246

How can I pass a &[u8] slice into that fn, that eats a &mut impl Read?

Add mut to the variable that holds the slice:

let vector = vec![1, 2, 3, 4];
let mut slice = &vector[..];
read_something(&mut slice);

Note that this makes the slice mutable, meaning it can be modified to refer to different data (which read_something() makes use of). It doesn't grant the slice ability to mutate the immutable vector.

How do these two cases differ? Why does the first example work even though the vector is not declared as mutable either?

In the first case the vector is not mutable, but the unnamed temporary that holds the slice is. Your first case desugars to something like:

let vector = vec![1, 2, 3, 4]; // immutable Vec
let mut tmp = &vector[..]; // mutable slice referring to immutable Vec
read_something(&mut tmp);
// here `tmp` may be pointing to different content
// (e.g. a subset of the vector, or something static)

The vector doesn't need to be mutable because the Read::read() impl for &[u8] doesn't attempt to modify the contents of the slice, it only modifies the slice (conceptually a <pointer, length> tuple) itself. It can do that because Read is implemented for &[u8], so when <&[u8] as Read>::read() receives &mut self, its full type is &mut &[u8] - a mutable reference to some slice of immutable data.

What Read::read() for &[u8] does is replace the received slice with a smaller slice which encompasses the as-yet-unread portion of the slice. (This allows "the same" slice to be passed to read() to eventually exhaust all data.) Since the 8-byte buffer you read into is larger than the 4-byte slice that serves as reading source, the modified tmp slice will be empty after read_something() returns (playground).

Upvotes: 2

Related Questions