“Expected trait A, found &A” when trying to box a trait object

“Expected trait A, found &A” when trying to box a trait object



I'm trying to make a trait that can either retrieve (and return a reference to) a trait object of another trait, or create one (and return a boxed version of it), leaving the choice to the implementor (which means I need to restrict the returned object's lifetime to that of the producer). However, I'm running into errors:


use std::borrow::Borrow;
use std::collections::HashMap;

trait A
fn foobar(&self)
println!("!");



trait ProducerOrContainer
fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>>;


impl<'b, B: Borrow<A>> ProducerOrContainer for HashMap<&'b str, B>
fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>> borrow



The error is:


error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | self.get(name).map(|borrow| Box::new(borrow.borrow()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait A, found &A
|
= note: expected type `std::option::Option<std::boxed::Box<dyn A + 'a>>`
found type `std::option::Option<std::boxed::Box<&dyn A>>`



Which puzzles me, because I'd expect a &A to be an A too. I've tried to impl<'a> A for &'a A, but that doesn't help either. Is there any way to fix this?


&A


A


impl<'a> A for &'a A






A implementing a trait does not mean that &A does. It sometimes can seem like that's true because of Rust's auto-ref and auto-deref behaviour. For example, it will auto-deref a variable so you can type foo.bar() instead of (*foo).bar().

– Peter Hall
Sep 16 '18 at 10:42


A


&A


foo.bar()


(*foo).bar()






Why would want to return a Box<&dyn A> anyway? Boxing a reference doesn't seem very useful.

– Peter Hall
Sep 16 '18 at 10:53


Box<&dyn A>






I noticed that name: &'a str is unnecessarily constrained here, because the returned reference is not tied to name. Maybe you have other implementations of ProducerOrContainer where the returned value is derived from name, but if not, getting rid of the 'a will be more flexible.

– trentcl
Sep 16 '18 at 13:06


name: &'a str


name


ProducerOrContainer


name


'a




2 Answers
2



...that can either retrieve (and return a reference to) a trait object of another trait, or create one (and return a boxed version of it).



With this requirement, a Box will not work. A Box owns its data, but you sometimes have borrowed data, which you can't move.


Box


Box



There is a type in the standard library called Cow, which is an abstraction over whether a value is borrowed or owned. However, it may not be quite suitable for you here because it won't let you own the data as a Box and it also requires that your data type must implement ToOwned.


Cow


Box


ToOwned



But we can take your requirement and model it directly as an enum:


enum


enum BoxOrBorrow<'a, T: 'a + ?Sized>
Boxed(Box<T>),
Borrowed(&'a T),



And make it ergonomic to use by implementing Deref:


Deref


use std::ops::Deref;

impl<'a, T> Deref for BoxOrBorrow<'a, T>
type Target = T;
fn deref(&self) -> &T
match self
BoxOrBorrow::Boxed(b) => &b,
BoxOrBorrow::Borrowed(b) => &b,





This lets you treat the custom BoxOrBorrow type as any other reference - you can dereference it with * or pass it to any function that expects a reference to T.


BoxOrBorrow


*


T



This is what your code would look like:


trait ProducerOrContainer
fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>>;


impl<'b, B: Borrow<dyn A>> ProducerOrContainer for HashMap<&'b str, B>
fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>>
self.get(name)
.map(



You can make the original code compile by implementing A for &'_ dyn A and adding an explicit cast:


A


&'_ dyn A


self.get(name).map(|borrow| Box::new(borrow.borrow()) as Box<dyn A>)



A closure is not a coercion site. The compiler looks at the contents of the closure to see what the return value is, and concludes that it returns Box<&'a dyn A>. But the closure itself cannot be coerced from "function returning Box<&'a dyn A>" to "function returning Box<dyn A + 'a>", because those types are structurally different. You add the cast to tell the compiler that you wanted the closure to return Box<dyn A> in the first place.


Box<&'a dyn A>


Box<&'a dyn A>


Box<dyn A + 'a>


Box<dyn A>



But this is a bit silly. Boxing a reference is completely unnecessary here, and casting it to Box<dyn A> just adds another level of indirection for the caller. It would be better to return a type that encapsulates the idea of "either a boxed trait object, or a reference to a trait object", as Peter Hall's answer describes.


Box


Box<dyn A>



In a future version of Rust, with generic associated types ("GATs"), it will be possible to make the return type an associated type of ProducerOrContainer, something like the following:


ProducerOrContainer


trait ProducerOrContainer
type Result<'a>: A;
fn get_a<'a>(&'a self, name: &'a str) -> Option<Result<'a>>;



With this trait definition, each type that implements ProducerOrContainer can choose what type it returns, so you can pick Box<dyn A> for some impls and &'a dyn A for others. However, this is not possible in current Rust (1.29).


ProducerOrContainer


Box<dyn A>


impl


&'a dyn A



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)