What does "Sized is not implemented" mean?
Asked Answered
C

2

55

I wrote the following code:

use std::io::{IoResult, Writer};
use std::io::stdio;

fn main() {
    let h = |&: w: &mut Writer| -> IoResult<()> {
        writeln!(w, "foo")
    };
    let _ = h.handle(&mut stdio::stdout());
}

trait Handler<W> where W: Writer {
    fn handle(&self, &mut W) -> IoResult<()>;
}

impl<W, F> Handler<W> for F
where W: Writer, F: Fn(&mut W) -> IoResult<()> {
    fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
}

And then rustc in my terminal:

$ rustc writer_handler.rs
writer_handler.rs:8:15: 8:43 error: the trait `core::marker::Sized` is not implemented for the type `std::io::Writer`
writer_handler.rs:8     let _ = h.handle(&mut stdio::stdout());
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
writer_handler.rs:8:15: 8:43 error: the trait `core::marker::Sized` is not implemented for the type `std::io::Writer`
writer_handler.rs:8     let _ = h.handle(&mut stdio::stdout());
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Why is this Writer required to implement Sized? It appears to me that the Sized is not needed. What I should do while keeping trait Handler to have this generic argument?


In Rust 1.0, this similar code produces the same problem:

use std::io::{self, Write};

fn main() {
    handle(&mut io::stdout());
}

fn handle(w: &mut Write) -> io::Result<()> {
    handler(w)
}

fn handler<W>(w: &mut W) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "foo")
}

With the error:

error[E0277]: the trait bound `std::io::Write: std::marker::Sized` is not satisfied
 --> src/main.rs:8:5
  |
8 |     handler(w)
  |     ^^^^^^^ `std::io::Write` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `std::io::Write`
  = note: required by `handler`

Later versions of Rust have the error

error[E0277]: the size for values of type `dyn std::io::Write` cannot be known at compilation time
  --> src/main.rs:8:13
   |
8  |     handler(w)
   |             ^ doesn't have a size known at compile-time
...
11 | fn handler<W>(w: &mut W) -> io::Result<()>
   |    ------- - required by this bound in `handler`
   |
   = help: the trait `std::marker::Sized` is not implemented for `dyn std::io::Write`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
Chacha answered 20/1, 2015 at 11:27 Comment(0)
A
52

The Sized trait is rather special, so special that it is a default bound on type parameters in most situations. It represents values that have a fixed size known at compile time, like u8 (1 byte) or &u32 (8 bytes on a platform with 64-bit pointers) etc. These values are flexible: they can be placed on the stack and moved onto the heap, and generally passed around by-value, as the compiler knows how much space it needs where-ever the value goes.

Types that aren't sized are much more restricted, and a value of type Writer isn't sized: it represents, abstractly, some unspecified type that implements Writer, with no knowledge of what the actual type is. Since the actual type isn't known, the size can't be known: some large types are Writers, some small types are. Writer is one example of a trait object, which at the moment, can only appear in executed code behind a pointer. Common examples include &Writer, &mut Writer, or Box<Writer>.

This explains why Sized is the default: it is often what one wants.

In any case, for your code, this is popping up because you're using handle with h, which is a Fn(&mut Writer) -> IoResult<()>. If we match this against the F: Fn(&mut W) -> IoResult<()> type that Handle is implemented for we find that W = Writer, that is, we're trying to use handle with the trait object &mut Writer, not a &mut W for some concrete type W. This is illegal because the W parameters in both the trait and the impl are defaulting to have a Sized bound, if we manually override it with ?Sized then everything works fine:

use std::io::{IoResult, Writer};
use std::io::stdio;

fn main() {
    let h = |&: w: &mut Writer| -> IoResult<()> {
        writeln!(w, "foo")
    };
    let _ = h.handle(&mut stdio::stdout());
}

trait Handler<W: ?Sized> where W: Writer {
    fn handle(&self, &mut W) -> IoResult<()>;
}

impl<W: ?Sized, F> Handler<W> for F
where W: Writer, F: Fn(&mut W) -> IoResult<()> {
    fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
}

And for the Rust 1.0 code:

use std::io::{self, Write};

fn main() {
    handle(&mut io::stdout());
}

fn handle(w: &mut Write) -> io::Result<()> {
    handler(w)
}

fn handler<W: ?Sized>(w: &mut W) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "foo")
}

I also wrote a blog post about Sized and trait objects in general which has a little more detail.

Andreaandreana answered 20/1, 2015 at 12:20 Comment(3)
Thanks a lot. I confirm your code works fine. It's my understanding that Sized implementation required because the rust runtime need to know how many bytes it takes on the call stack. So, I was going to avoid unknown sized types using pointer, in this case &mut... I cannot understand where are Sized required in my code yet.Chacha
It isn't required, as the second set of code working demonstrates, it is just a question of defaults: the compiler is defaulting to putting a Sized bound on that type parameter, because that's what the compiler is defined to do for almost all type parameters.Andreaandreana
Oh. I got it! I'm sorry that I did not fully understand your answer: "the impl are defaulting to have a Sized bound". Thanks again, dbaupp!Chacha
D
6

First of all, note that h is of a type that implements Fn(&mut Writer) -> IoResult<()>.

h.handle is being called; this depends, then, on the Handler implementation where W is Writer—note that carefully: W is Writer, an unsized type. The &mut stdio::stdout() is therefore going to be cast to the &mut Writer trait object. This is all very well in theory, but when brought back to the implementation falls down. When it comes to constraints, they are sized by default, and so it complains that Writer, the value you are trying to assign for W, is not sized.

There are two primary solutions here:

  1. Switch to using the concrete writer type on h so that you’re dealing with a sized type:

    use std::io::{IoResult, Writer, stdio, LineBufferedWriter};
    use std::io::stdio::StdWriter;
    
    fn main() {
        let h = |&: w: &mut LineBufferedWriter<StdWriter>| -> IoResult<()> {
            writeln!(w, "foo")
        };
        let _ = h.handle(&mut stdio::stdout());
    }
    
    trait Handler<W> where W: Writer {
        fn handle(&self, &mut W) -> IoResult<()>;
    }
    
    impl<W, F> Handler<W> for F
    where W: Writer, F: Fn(&mut W) -> IoResult<()> {
        fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
    }
    
  2. Permit W to be an unsized type. This is acceptable as you only use it through a reference &mut W. If you wished to use it as a bare type, e.g. a method taking W by value, it would not do.

    use std::io::{IoResult, Writer};
    use std::io::stdio;
    
    fn main() {
        let h = |&: w: &mut Writer| -> IoResult<()> {
            writeln!(w, "foo")
        };
        let _ = h.handle(&mut stdio::stdout());
    }
    
    trait Handler<W: ?Sized> where W: Writer {
        fn handle(&self, &mut W) -> IoResult<()>;
    }
    
    impl<W: ?Sized, F> Handler<W> for F
    where W: Writer, F: Fn(&mut W) -> IoResult<()> {
        fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
    }
    

I would recommend supporting an unsized W even if you don’t then use that in this case—there’s no reason it needs to be sized.

Dampen answered 20/1, 2015 at 12:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.