How to describe the return type from a boxed list of dyn A?

I am trying to store a boxed list of a dyn trait A and then implement an extension trait to make it easy to retrieve a particular impl of A. My helper trait returns &Box<dyn A> but clippy is yelling at me telling me not to use &Box

For example, I have a trait like Foo and multiple implementations

trait Foo {
  fn matches(&self, b: &Bar) -> bool;
  fn do_thing(&self, b: &Bar);
}

// Assume several impl's of Foo called Fiz, Fuz, and Fez

let foos: Vec<Box<dyn Foo>> = vec![
  Box::new(Fiz::new(...)),
  Box::new(Fuz::new(...)),
  Box::new(Fez::new(...)),
];

I then try to define a helper trait like

pub trait FindFoo {
  fn find_foo(b: Bar) -> Option<&Box<dyn Foo>>;
}

impl FindFoo for Vec<Box<dyn Foo>> {
  fn find_foo(&self, bar: &Bar) -> Option<&Box<dyn Foo>> {
    self.iter().find(|foo| foo.matches(bar))
  }
}

This works, but clippy is telling me

you seem to be trying to use `&Box<T>`. Consider using just `&T`
`#[warn(clippy::borrowed_box)]` on by default
for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box

If I remove the reference from the return type I get all kinds of errors. For example,

// Updating the trait and leaving out the boilerplate 

fn find_foo(&self, b: &Bar) -> Option<Box<dyn Foo>> {
  self.iter().find(|foo| foo.matches(bar)).as_deref()
} 

Results in the mismatches types error

mismatched types
expected enum `std::option::Option<std::boxed::Box<(dyn Foo + 'static)>>`
   found enum `std::option::Option<&std::boxed::Box<dyn Foo>>`

I've considered cloning (Option::cloned) and copying (Option::copied) but these don't really solve the problem. Do I need to to use an Rc here so my vec can keep a reference to my dyn Foo on the heap and hand out a reference as needed?

Any tips on a better way to do this is appreciated :pray: Thank you!

You should use .as_deref(), and change the signature to fn find_foo(b: Bar) -> Option<&dyn Foo>;

Edit: Oh as_deref() will not work here. Use .map(|b| &**b).

2 Likes

This works, thank you!

To confirm my understanding, &**b

  1. Derefs Option<&Box<dyn Foo>> to &Box<dyn Foo> via * #1
  2. Derefs &Box<dyn Foo> to dyn Foo via * #2
  3. Returns a reference to dyn Foo via & (after deref'ing twice)

At step 2 is &Box<dyn Foo> implicitly dereferenced to Box<dyn Foo>?

The original option has type Option<&Box<dyn Foo>>, so b has type &Box<dyn Foo>. Then:

  1. *b has type Box<dyn Foo>.
  2. **b has type dyn Foo, via Box::deref().
  3. &**b has type &dyn Foo.

Note the intermediate values are only accessed by reference, so we don't actually need to own the Box.

3 Likes

This makes sense. I was forgetting that map is going to give me the inner value. Thank you :pray: