Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make my own adapter methods able to consume a trait object?

Tags:

rust

traits

I have a trait that has "adapter methods" that consume the method:

struct Bar<T>(T);

trait Foo {
    fn make_bar(self) -> Bar<Self>
    where
        Self: Sized,
    {
        Bar(self)
    }
}

impl Foo for u8 {}
impl Foo for bool {}

This is modeled after Iterator. When I use a Box<Iterator>, I can still call Iterator adapter methods like map or filter:

fn main() {
    let a = vec![1, 2, 3];
    let b: Box<Iterator<Item = u8>> = Box::new(a.into_iter());
    let c = b.map(|x| x * 2);
    for z in c {
        println!("{}", z)
    }
}

However, the methods I've defined don't work in the same way:

fn main() {
    let a: Box<Foo> = Box::new(42);
    a.make_bar();
}

This fails with the errors

error[E0277]: the trait bound `Foo: std::marker::Sized` is not satisfied
  --> src/main.rs:18:7
   |
18 |     a.make_bar();
   |       ^^^^^^^^ `Foo` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `Foo`

It's absolutely correct that Foo does not have a size - it's a trait. However, Box<Foo> should have a size, as I understand it. What is Iterator doing differently from my code?

like image 672
Shepmaster Avatar asked Jun 04 '15 02:06

Shepmaster


1 Answers

Because Iterator is object safe, the trait object Iterator implements Iterator.

Because impl<I: Iterator + ?Sized> Iterator for Box<I>, the boxed trait object Box<Iterator> implements Iterator.

Thus in your case the solution is to implement Foo for Box<Foo>. This could be generic like Iterator’s (impl<T: ?Sized + Foo> Foo for Box<T> { }) or it could be specific (impl Foo for Box<Foo> { }).

like image 197
Chris Morgan Avatar answered Oct 02 '22 16:10

Chris Morgan