I am encountering a compiler error for something that I feel should work.
I tried this code (note generators are nightly-only at the time of writing):
#![feature(generators, generator_trait)]
use std::ops::Generator;
struct Value {}
struct Container<G: Generator<Yield = Value, Return = ()>> {
generator: Box<G>,
}
impl Container<Box<Generator<Yield = Value, Return = ()>>> {
pub fn new(&mut self) -> Box<Self> {
let generator: Box<Generator<Yield = Value, Return = ()>> = Box::new(|| loop {
yield Value {}
});
Box::new(Container {
generator: generator,
})
}
}
fn main() {}
where I get this error:
error[E0308]: mismatched types
--> src/main.rs:20:24
|
20 | generator: generator,
| ^^^^^^^^^ expected struct `std::boxed::Box`, found trait std::ops::Generator
|
= note: expected type `std::boxed::Box<std::boxed::Box<std::ops::Generator<Yield=Value, Return=()>>>`
found type `std::boxed::Box<std::ops::Generator<Yield=Value, Return=()>>`
error: aborting due to previous error
I don't understand why two levels of boxing are expected here, I only asked for one (Box<G>
).
It looks like Generator
is indeed a trait, not an alias for Box<...>
. I can't think of other explanations.
I can easily resolve the error by replacing Box<G>
by G
, but I want to know why my way does not work (could it be a bug?).
Nightly version 1.28.0-nightly (2018-06-15 967c1f3be1c9ce0469ae) in debug mode on the playground (but I have a similar issue locally with more complex code).
There are a couple of problems here.
First, you define Containter<G>
as having a member of type Box<G>
. Then you write the impl
for Containter<Box<G>>
, that naturally has a member of type Box<Box<G>>
. Probably you just want:
impl Container<Generator<Yield = Value, Return = ()>> {
...
}
Second, if you compile again you have this error:
|
16 | Box::new(Container {
| ^^^^^^^^^ `std::ops::Generator<Yield=Value, Return=()>` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `std::ops::Generator<Yield=Value, Return=()>`
note: required by `Container`
--> a.rs:7:1
|
7 | struct Container<G: Generator<Yield = Value, Return = ()>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This means that struct Containter
requires that G
is sized, but your generator doesn't implement it. That's true, you want Box<G>
to be a trait object, so G
will be unsized (a trait type). But type arguments in structs are Sized
by default. The solution is to add ?Sized
requirement to Container
:
struct Container<G: Generator<Yield = Value, Return = ()> + ?Sized> {
generator: Box<G>,
}
And now it compiles.
PS: If your Container
struct is to be used only with Generator
trait objects it is far easier to get rid of the generic arguments and just write:
struct Container {
generator: Box<Generator<Yield = Value, Return = ()>>,
}
impl Container {
pub fn new(&mut self) -> Box<Self> {
let generator = Box::new(|| loop {
yield Value {}
});
Box::new(Container {
generator: generator,
})
}
}