Reputation: 8022
So I'm getting the following error:
error[E0277]: the size for values of type 'dyn Getter' cannot be known at compilation time
struct PlusFive;
impl Operator for PlusFive {
fn apply<'a>(&self, a: &'a dyn Getter) -> Box<dyn Getter + 'a> {
Box::new(MapGetter {
source: a, // A pointer to some Getter
f: |n:i32| n + 5 // A way to later compute +5 on source
});
}
}
Sized
is not implemented for dyn Getter
Getter
is implemented for MapGetter<T, F>
I'm not sure what this errors means or how to resolve it. The size dyn Getter
can't be known, but the size of MapGetter certainly can! and Since MapGetter is a concrete type that implements the Getter trait, I don't see why I can't Box this up and return it.
I'm missing something here. I can Box up the MapGetter
, what I can't do is lift it into a trait object?
Here's the full example I'm working with if it helps to see it all in context:
On the whole, I've been trying to do this with dynamic dispatch. In part because I want to see what can be done and in part because I foresee myself wanting a list of dyn Operator
s where the concrete types underneath may vary.
I don't want to attach operators to the Getter
trait directly because eventually I want the operators to represent re-usable pieces of logic, so they can be applied to a Getter
after the fact (or more than once)
trait Getter {
fn compute(&self) -> i32;
fn pipe(&self, operator: &dyn Operator) -> Box<dyn Getter>
where
Self: Sized,
{
operator.apply(&self)
}
}
impl<T: Getter> Getter for &T {
fn compute(&self) -> i32 {
(*self).compute()
}
}
impl<T: Getter> Getter for Box<T> {
fn compute(&self) -> i32 {
(*self).compute()
}
}
struct PureGetter<T>(T);
impl Getter for PureGetter<i32> {
fn compute(&self) -> i32 {
self.0
}
}
struct MapGetter<T, F> {
source: T,
f: F,
}
impl<T, F> Getter for MapGetter<T, F>
where
T: Getter,
F: FnMut(i32) -> i32 + Clone,
{
fn compute(&self) -> i32 {
(self.f.clone())(self.source.compute())
}
}
trait Operator {
fn apply<'a>(&self, a: &'a dyn Getter) -> Box<dyn Getter + 'a>;
}
struct PlusFive;
impl Operator for PlusFive {
fn apply<'a>(&self, a: &'a dyn Getter) -> Box<dyn Getter + 'a> {
Box::new(MapGetter {
source: a,
f: |n:i32| n + 5
})
}
}
fn main() {
let result = PureGetter(0).pipe(&PlusFive).compute();
println!("{:#?}", result);
}
error[E0277]: the size for values of type `dyn Getter` cannot be known at compilation time
--> src/main.rs:71:9
|
71 | / Box::new(MapGetter {
72 | | source: a,
73 | | f: |n:i32| n + 5
74 | | })
| |__________^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn Getter`
= help: the trait `Getter` is implemented for `MapGetter<T, F>`
note: required for `&dyn Getter` to implement `Getter`
--> src/main.rs:24:17
|
24 | impl<T: Getter> Getter for &T {
| ^^^^^^ ^^
= note: 1 redundant requirement hidden
= note: required for `MapGetter<&dyn Getter, [closure@src/main.rs:73:16: 73:23]>` to implement `Getter`
= note: required for the cast from `MapGetter<&dyn Getter, [closure@src/main.rs:73:16: 73:23]>` to the object type `dyn Getter`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `messin` due to previous error
Upvotes: 1
Views: 292
Reputation: 10218
Minimized example for this specific error:
trait Getter {}
impl<T: Getter> Getter for &T {}
impl Getter for () {}
fn main() {
let getter: &dyn Getter = &();
let getter: Box<dyn Getter> = Box::new(getter);
}
The reason is the following:
Box<dyn Getter>
from Box<&dyn Getter>
, &dyn Getter
must implement Getter
itself.&T
has a blanket implementation for Getter
.Sized
bound, therefore it can't be applied to &dyn Getter
.To fix the immediate problem, you can simply relax the bound:
impl<T: Getter + ?Sized> Getter for &T {}
In your original code, the reasoning is similar, just with one more intermediate step - you have to pass impl Getter
to MapGetter
, so that MapGetter
is Getter
itself; but the passed-in value is &dyn Getter
, which isn't Getter
for the reasons above.
As an aside, I'd say that it is quite unidiomatic to have both references and Box
es at the same time. You'd probably have an easier way if you restricted yourself to only one of them - either loaning the temporary references all the way down, or Box
ing everything to own everything.
Upvotes: 4