Implementating a trait for struct reference

Hi,

I want to implement a trait both for a struct reference and for struct value (as usual) of a struct, but I'm struggling implementing a solution working for both, here it is my code:

trait Foo {
    fn foo(&self);
}

struct ByRef;

impl<'a> Foo for &'a ByRef {
    fn foo(self) {
    }
}

struct ByVal;

impl Foo for ByVal {
    fn foo(&self) {
    }
}

Since I am implementing &'a ByRef I was expecting that self was actually referring to &self.

If I change my Foo::foo(&self) to Foo:foo(self) ByRef compiles, but now ByVal is not compiling anymore (and it makes sense).

This is a simplified example, actually I was trying to create a subtrait of IntoIterator, but then I realized that I had to remove all references from my trait interface in order to get it to work and now my trait interface looks strange to me (it seems that it moves everything)! You can see a simplified example here.

Than you

1 Like

If Self is &ByRef, then &Self is &&ByRef. Rust doesn't "flatten" references. There's a bit of "magic" around auto-deref in . operator that may seem like Rust doesn't nest references, but that's only happening at a method call site, and it doesn't affect the type system directly.

Implementations of a trait must use exact same methods as the trait, so if the trait has &self, you have to keep using &self, even if it means getting &&ByRef.

You should probably redefine the trait to use foo(self) and rely on the fact that shared references are copyable.

Or keep using &self, and implement trait for ByRef, which will give you &ByRef in &self.

8 Likes

Thanks for the clarification, I was forgetting that Self is what it is specified after for :blush:

Anyway I have another question related to this topic (so I am not opening a new thread).

What if I have a trait with a method taking self as reference and another taking as mut reference and I want to implement that trait for &ByRef?

Here my bad attempt :crazy_face: (it is clear that this is a wrong way):

struct SomeMegaComplexData;

trait Foo {
    fn via_ref(&self) -> SomeMegaComplexData;
    fn via_mut_ref(&mut self) -> SomeMegaComplexData;
}

struct ByRef {
    data: i32,
}

impl<'a> Foo for &'a ByRef {
    fn via_ref(&self) -> SomeMegaComplexData {
        // Complex computation here
        SomeMegaComplexData
    }
    fn via_mut_ref(&mut self) -> SomeMegaComplexData {
        // Cannot assign data, since self is behind & borrow (not &mut borrow)
        // This code here doesn't make any sense and should not never use
        SomeMegaComplexData
    }
}

impl<'a> Foo for &'a mut ByRef {
    fn via_ref(&self) -> SomeMegaComplexData {
        // Should I copy and paste complex computation of code above???
        SomeMegaComplexData
    }
    fn via_mut_ref(&mut self) -> SomeMegaComplexData {
        self.data = 5;
        SomeMegaComplexData
    }
}

fn main() {
    // Using &mut
    let mut mr = &mut ByRef{data: 0}; // I need to use "let mut b", instead "let b"
                                      // since I guess in line 29 Self is &mut ByRef, so
                                      // &mut Self = mut &mut ByRef? Uhm...
    let _x = mr.via_ref();
    let _y = mr.via_mut_ref();
    
    // Using &
    let r = &ByRef{data: 0};
    let _x = mr.via_ref();
    let _y = mr.via_mut_ref();        // Argh... I can use via_mut_ref here! So strange!
}

Thank you.

Why you don't impl Foo for ByRef directly here? It's not clear from this example.

1 Like

You just can't. For a & borrow of a primitive type like i32, the language guarantees that it's absolutely impossible to change it no matter what.

&mut &T allows you to replace one immutable reference with another similar immutable reference borrowed at the same time (so typically you can't replace it with a reference to any new data), and the T type is borrowed immutably.

There's interior mutability, so you could modify data inside &Mutex<i32>, but when you have &ByRef {i32} there's no way.

That was maybe a bad example... Consider this example, here I want to implement a trait that have method that borrow as mutable self, but it is also a subtrait of IntoIterator:

struct MyItem;

// I cannot understand why I needed to change the train bound here from
// <Item=MyItem> to <Item=&'a MyItem> as I have implemented MyList for &StupidList
trait MyList<'a>: IntoIterator<Item=&'a MyItem> {
    fn set_elem_at(&mut self, i: usize, item: MyItem);
}

struct StupidList {
    vec: Vec<MyItem>,
}

impl<'a> MyList<'a> for &'a StupidList {
    // This doesn't complie because of this error:
    // "cannot borrow `self.vec` as mutable, as it is behind a `&` reference"
    // So I should implement MyList for &mut StupidList, and then also IntoIterator
    // That would mean that I will only able to iterate over StupidList only
    // using mutable reference to it... Even if I just want to call non mutable method!
    fn set_elem_at(&mut self, i: usize, item: MyItem) {
        self.vec[i] = item;
    }
}

impl<'a> IntoIterator for &'a StupidList {
    type Item = &'a MyItem;
    type IntoIter =  std::slice::Iter<'a, MyItem>;
    
    fn into_iter(self) -> Self::IntoIter {
        self.vec.iter()
    }
}

fn main() {
    let s = StupidList{vec: vec![]};
    
    for _ in &s {
    }
}

So this code doesn't compile, to fix it I should implement MyList and IntoIterator for &mut instead of & , doing that it means that I cannot iterate over a normal reference, but I have always to use a mutable reference even if I am iterating without using any mutable method.

P.S.:
Another thing that is not clear to me is why I need to change the trait bound (from MyItem to &MyItem) in MyList as soon I have implemented MyList for &StupidList?

I would suggest:

-trait MyList<'a>: IntoIterator<Item=&'a MyItem> {
+trait MyList where for<'a> &'a Self: IntoIterator<Item=&'a MyItem> {
     fn set_elem_at(&mut self, i: usize, item: MyItem);
 }

 struct StupidList {
     vec: Vec<MyItem>,
 }

-impl<'a> MyList<'a> for &'a StupidList {
+impl MyList for StupidList {

More comments in the link. More on HRTB.

1 Like

So... If I got it right what you have done is adding a trait bound not on the trait itself, but on a reference to it (using for<'a> &'a Self) bounding that to be a subtype of IntoIterator<Item=&'a MyItem>. where 'a could be any lifetime... Did I get it right?

Wow... I mean... Wow! :sweat_smile:

Honestly I need to study more these advanced Rust topics, I hope that somebody will publish a book/resource where also this kind of details are clearly explained.
I'm currently studying on last Jim Blandy's book and it is very good, but it lacks of advanced topics.

1 Like

...bounding that to be a subtype an implementer of...

But that nit aside, yes, you got it right.


Connecting this back to the other thread -- if you end up trying to mix where clauses like this that aren't supertraits, and dyn Trait, it sadly doesn't go well. So, to just continue dumping advanced topics out there I guess, here's one way to get the requirement onto &dyn MyList. There are probably other ways.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.