Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Borrow checker failing when using traits as type parameter

I have a problem with the borrow checker when using traits as type parameter in a structure:

trait Trait {}

struct FooBar;
impl Trait for FooBar{}

struct Observer<Arg> {
    action: Box<Fn(Arg) + Send>,
    // Other fields
}

impl <Arg> Observer<Arg> {
    fn new(action: Box<Fn(Arg) + Send>) -> Observer<Arg> {
        Observer{action: action}
    }

    fn execute(&self, arg: Arg) {
        (*self.action)(arg);
    }
}

fn test() {
    let mut foobar = FooBar;
    {
        let mut observer = Observer::new(Box::new(|&: param: &mut Trait| {
            // do something with param here
        }));
        observer.execute(&mut foobar);   // First borrow passes ...
        observer.execute(&mut foobar);   // This fails as "foobar" is already borrowed
    }   // The previous borrow ends here (lifetime of "observer")
}

The output is:

error: cannot borrow `foobar` as mutable more than once at a time
    observer.execute(&mut foobar);   // This fails as "foobar" is already borrowed
                          ^~~~~~
note: previous borrow of `foobar` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foobar` until the borrow ends
    observer.execute(&mut foobar);   // First borrow passes ...
                          ^~~~~~
note: previous borrow ends here
 {
...
 }   // The previous borrow ends here (lifetime of "observer")
 ^

Yet the following example works:

trait Trait {}

struct FooBar;
impl Trait for FooBar{}

struct Observer {
    action: Box<Fn(&mut Trait) + Send>,
    // Other fields
}

impl Observer {
    fn new(action: Box<Fn(&mut Trait) + Send>) -> Observer {
        Observer{action: action}
    }

    fn execute(&self, arg: &mut Trait) {
        (*self.action)(arg);
    }
}

fn test() {
    let mut foobar = FooBar;
    {
        let mut observer = Observer::new(Box::new(|&: param: &mut Trait| {
            // do something with param here
        }));
        observer.execute(&mut foobar);
        observer.execute(&mut foobar);
    }
}

This looks really weird to me, as the second example is just an instantiation of the first example, and I could probably (painfully) implement the same thing with macros.

I guess this is quite tricky, as I need to know the type of the parameter taken by the closure, but I don't need to store this reference ...

Is this a bug in the borrow checker? Or am I doing something wrong?

rustc 1.0.0-nightly (44a287e6e 2015-01-08 17:03:40 -0800)

EDIT 1: Precised the use case

EDIT 2: As explained in the answer below, the issue is that the borrow checker forces the lifetime of Observer<&mut Type> to be the same as the &mut Type, so in fact the issue is not related to the fact that we use a trait as a type parameter (it does the same with an actual structure).
So in my case, I can have a workaround by defining Observer<Arg> like this:

struct Observer<Arg> {
    action: Box<Fn(&mut Arg) + Send>,
}

so the type parameter Arg itself is not a reference, but this makes the code less generic. Anybody has a better solution?

like image 307
Vaelden Avatar asked Oct 20 '22 19:10

Vaelden


1 Answers

The problem here is that the borrow checker is forcing the lifetime of the &mut Trait reference to be the same as the whole GenericStruct. I believe this is because the reference is a type parameter of the struct itself.

Since your struct has no fields that store the reference (if you need to do this in your original code, please update your question), then you can move the type parameter to the method itself, instead of the struct:

trait Trait{}

struct FooBar;
impl Trait for FooBar{}

struct GenericStruct;

impl GenericStruct {
    fn bar<T>(&self, _: T) {}
}

fn main() {
    let mut foobar = FooBar;

    {
        let foo = GenericStruct;
        foo.bar(&mut foobar);
        foo.bar(&mut foobar);
    }
}

This will make the borrow last only as long as the call to foo.bar().

like image 168
Renato Zannon Avatar answered Oct 22 '22 22:10

Renato Zannon