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`
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 Writer
s, 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.
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:
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) }
}
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.