Emanuel
Emanuel

Reputation: 881

Casting &[u8] to std::io::Read causes Sized issue

Trying to provide &[u8] as argument to a function requiring Read doesn't seem to work as I expected, as illustrated by the below example.

use std::io::Read;

fn main() {
    let bytes: &[u8] = &[1, 2, 3, 4];
    print_reader(&bytes);
}

fn print_reader(reader: &(Read + Sized)) {
    for byte in reader.bytes() {
        println!("{}", byte.unwrap());
    }
}

Compiler error:

error: the trait bound `std::io::Read + Sized: std::marker::Sized` is not satisfied [--explain E0277]
 --> <anon>:9:24
9 |>     for byte in reader.bytes() {
  |>                        ^^^^^
note: `std::io::Read + Sized` does not have a constant size known at compile-time

error: the trait bound `std::io::Read + Sized: std::marker::Sized` is not satisfied [--explain E0277]
 --> <anon>:9:5
9 |>     for byte in reader.bytes() {
  |>     ^
note: `std::io::Read + Sized` does not have a constant size known at compile-time
note: required because of the requirements on the impl of `std::iter::Iterator` for `std::io::Bytes<std::io::Read + Sized>`

error: aborting due to 2 previous errors

Rust playground

The following trait implementation can be found in the std::slice documentation:

impl<'a> Read for &'a [u8].

Upvotes: 2

Views: 717

Answers (1)

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 88636

I think this is a rather an unhelpful error message. I'll try to explain:


First: you can't have a trait object &Sized. This violates the first object safety rule and it doesn't really make sense either. The only reason to add the Sized trait bound is to use the special property of all Sized types (e.g. saving it on the stack). Look at this example trying to use the property:

fn foo(x: &Sized) {
    let y = *x;
}

What size would y have? The compiler can't know, as with any other trait object. So we're not able to use the only purpose of Sized with trait objects. Thus a trait object &Sized is useless and can't really exist.

In this case the error message at least kind of tells us the correct thing:

error: the trait `std::marker::Sized` cannot be made into an object [--explain E0038]
 --> <anon>:7:1
7 |> fn foo(x: &Sized) {
  |> ^
note: the trait cannot require that `Self : Sized` 

Furthermore: I suspect you added the + Sized bound to work around the same error, which already showed up when you had the argument reader: &Read. Here is one important insight from the detailed error description:

Generally, Self : Sized is used to indicate that the trait should not be used as a trait object.

This restriction for Read::bytes does make sense, because the Bytes iterator calls Read::read() once for every single byte. If this function call would be a virtual/dynamic one, the overhead for the function call would be much higher than the actual process of reading the byte.


So... why do you need to have Read as a trait object anyway? Often it's sufficient (and in any case much faster) to handle this via generics:

fn print_reader<R: Read>(reader: R) {
    for byte in reader.bytes() {
        println!("{}", byte.unwrap());
    }
}

This avoids dynamic dispatch and works nicely with the type checker and the optimizer.

Upvotes: 2

Related Questions