Borrow <Trait> implementation for a type that implements Trait
Let's say I have some kind of trait:
trait MyTrait { fn function1(&self); } and some type that implements it:
struct MyStruct { number: i32, } impl MyTrait for MyStruct { fn function1(&self) { printn!("{}", self.number); } } Now I have another type that wants to take things that implement MyTrait . They do not care whether they belong or not. From reading around, it sounds like the right way to achieve this so that it takes Borrow<X> instead of X or &X or whatever. This allows you to use things like X or Rc<X> or Box<X> , etc.
It works for me when X is a specific type, but how to make it work when X is a sign?
Here is what I tried first:
struct Consumer<T> { inner: T } impl<T: Borrow<MyTrait>> Consumer<T> { pub fn new(inner: T) -> Consumer<T> { Consumer { inner: inner } } pub fn do_stuff(&self) { self.inner.borrow().function1(); } } fn main() { // I want to eventually be able to swap this out for x = Rc::new(MyStruct ... // but I'll keep it simple for now. let x = MyStruct { number: 42 }; let c = Consumer::new(x); c.do_stuff(); } This does not work yet, because MyStruct implements Borrow<MyStruct> , but not Borrow<MyTrait> . Ok, so try to implement this:
impl Borrow<MyTrait> for MyStruct { fn borrow(&self) -> &MyTrait { self } } This gives me the following error, which I do not understand:
<anon>:33:5: 35:6 error: method `borrow` has an incompatible type for trait: expected bound lifetime parameter , found concrete lifetime [E0053] <anon>:33 fn borrow(&self) -> &MyTrait { <anon>:34 self <anon>:35 } <anon>:33:5: 35:6 help: see the detailed explanation for E0053 error: aborting due to previous error playpen: application terminated with error code 101 What? It does not have any specific terms of life, and Borrow is defined without mentioning the mentioned lives. I'm at a dead end.
First, do my assumptions agree that using Borrow is the right way? And if so, how do I implement the Borrow trait?
The correct way to write an implementation is as follows:
impl<'a> Borrow<MyTrait + 'a> for MyStruct { fn borrow(&self) -> &(MyTrait + 'a) { self } } Object objects can be limited to attachment to life. This is because the type that implements the attribute may contain references, and in some situations we need to be able to distinguish between an object that depends on borrowed objects from an object that does not. If no lifetime binding is specified, I think that it defaults to 'static ; however, specifying &(MyTrait + 'static) , because the type of the returned file is compiled (it is less general, so you have to approve the general solution above), so the problem you are facing is more subtle than this ...
As an aside, I found an alternative way to do this that does not require the implementation of Borrow<MyTrait> at all:
Instead of impl<T: Borrow<MyTrait> Consumer<T> we can make Consumer take an additional parameter that determines what the actual borrowed type will be, and then restrain this type to implement the trait. Like this:
impl<T: Borrow<Borrowed>, Borrowed: MyTrait> Consumer<T, Borrowed> { This requires Consumer have a PhantomData element that refers to an unused Borrowed type. Here's the full implementation:
struct Consumer<T, Borrowed> { inner: T, phantom: PhantomData<Borrowed> } impl<T: Borrow<Borrowed>, Borrowed: MyTrait> Consumer<T, Borrowed> { fn new(inner: T) -> Consumer<T, Borrowed> { Consumer { inner: inner, phantom: PhantomData } } pub fn do_stuff(&self) { // this function is the same as before. self.inner.borrow().function1(); } } This alternative has the nice property, which allows you to use traits with common methods in them, since this does not require the creation of any feature objects (feature objects cannot be created for features that have common functions: see https: // doc.rust-lang.org/error-index.html#method-has-generic-type-parameters ).
The only drawback is that Consumer now needs to give hints regarding its general parameters. You must tell it the specific type:
fn main() { let x = MyStruct { number: 42 }; let c = Consumer::<_, MyStruct>::new(x); c.do_stuff(); }