loggerhead
loggerhead

Reputation: 131

How to write a method that adds `self` as a mutable trait reference to a collection?

I have a trait Foo and a struct Bar. Bar has a Vec field which holds any reference that implements Foo.

trait Foo { }

struct Bar<'a> {
    handlers: Vec<&'a mut Foo>,
}

I have another struct Stu which implements Foo, and has a method add to add itself to bar.

struct Stu { }

impl Foo for Stu { }

impl Stu {
    fn add(&mut self, bar: &mut Bar) {
        bar.handlers.push(&mut self);
    }
}

Because there are many kinds of structs that implement Foo and the actions finished in add method are varied and I need a struct that holds all of them (which is Bar here), I wrote the above code. But the compiler complains:

the trait bound &mut Stu: Foo is not satisfied

How can I fix this or realize my desired end goal?

Upvotes: 0

Views: 542

Answers (2)

Matthieu M.
Matthieu M.

Reputation: 300289

Read the error method more carefully:

the trait bound &mut Stu: Foo is not satisfied

versus:

impl Foo for Stu { }

You have implemented Foo for Stu, not for &mut Stu.

In Rust, a value, immutable reference and mutable reference are three distinct entities, and you can implement a trait for either of them independently.


The second corner case is that &mut self is not pattern matching. In regular pattern matching, &mut a: &mut A means that a is of type A, and indeed the other parameters are so treated.

However, confusingly, &mut self is just syntactic sugar for self: &mut Self...

So the type of self here is &mut Stu, and therefore you should not qualify it further in the push call.


Therefore, you have to correct your code to:

impl Stu {
    fn add(&mut self, bar: &mut Bar) {
        bar.handlers.push(self);
    }
}

At this point, you will get an error about lifetime constraints: it is not guaranteed here that self will outlive Bar. You need to annotate it (use the same lifetime) to make it work:

impl Stu {
    fn add<'a>(&'a mut self, bar: &mut Bar<'a>) {
        bar.handlers.push(self);
    }
}

Note: when annotating lifetimes, it is unnecessary to annotate them all; it's only necessary to insert a name for those that you want to constrain.

Upvotes: 4

basic_bgnr
basic_bgnr

Reputation: 737

I've modified your program to include explicit lifetime <'a, 'b> which leads to successful compilation.

trait Foo { }

struct Bar<'a> {
    pub handlers: Vec<&'a mut Foo>,
}

struct Stu;

impl Foo for Stu { }

impl Stu {
    fn add<'a,'b> (&'a mut self, bar: &'b mut Bar<'a>) {
        bar.handlers.push(self);
    }
}
fn main(){
    println!("{:?}", "success");
}

Upvotes: 1

Related Questions