简体   繁体   中英

Boxed trait object is still an unknown size

The Error:

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
        });
    }
}
  • the trait Sized is not implemented for dyn Getter
  • the trait Getter is implemented for MapGetter<T, F>

The Question:

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:


Note:

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)

Full Context:

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,
        (*self).compute()
    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

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);
}

Playground

The reason is the following:

  • To create a Box<dyn Getter> from Box<&dyn Getter> , &dyn Getter must implement Getter itself.
  • &T has a blanket implementation for Getter .
  • But the generic parameter of this implementation has an implicit 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM