How to call two methods at the same time which respectively require mutable and immutable references?

To simplify my situation, let assume that we have a struct like this

struct Data { }
impl Data {
    fn report(&self) { ... }
    fn modify(&mut self) { ... }
    fn update(&mut self) {
        self.report();
        self.modify();
    }
}

This code works fine as I don't use mutable reference and immutable reference at the same time. However, later I want this struct to be a trait (to adapt other implementations), where I have to assign a lifetime parameter since some of the methods return a reference

trait Data<'a> {
    // assumes these methods has to return a reference
    fn report(&'a self) -> &'a u32 { ... }
    fn modify(&'a mut self) -> &'a u32 { ... }
    fn update(&'a mut self) {
        self.report();
        self.modify(); // error here
    }
}

In this case, the call to modify() in update() will fail since the first borrow of self has a lifetime 'a, which conflicts with modify().

The example here may seems not reasonable, but in my real problem, I have several methods that return an iterator on a member vector, where I have to give the std::slice::Iter a lifetime parameter.

Is there any solution such that I can call the two methods just like a struct/trait without lifetime annotation? Or is there any design problem in my example that I should avoid? Thanks for your help!

Why do you have this 'a lifetime bound on self, when it's also parameter on the trait? That means you have to borrow self for at least as long as the data it references, which means... borrow it for as long as it exists. You should just use &self, and only borrow self for as long as the method call that needs it.

1 Like

The reason is basically explained in this question. I need to assign a lifetime bound on self in order to be able to return an std::slice::Iter in my method.

A closer example would be like

trait Data<'a> {
    type Iter: Iterator<Item = &'a u32>;
    // assumes these methods has to return a reference
    fn report(&'a self) -> Self::Iter { ... }
    fn modify(&'a mut self) -> Self::Iter { ... }
    fn update(&'a mut self) {
        self.report();
        self.modify(); // error here
    }
}

It's impossible, because &mut is exclusive, and when you use it on 'a, the exclusivity is extended to the whole scope of 'a, which as far as this trait can see, borrowing it forever.

You can emulate GATs here to be able to get rid of the lifetime parameter on the trait:

trait HasIterator<'a> {
    type Iter: Iterator<Item = &'a u32>;
}

trait Data: for<'any> HasIterator<'any> {
    // assumes these methods has to return a reference
    fn report(&self) -> <Self as HasIterator<'_>>::Iter { todo!() }
    fn modify(&mut self) -> <Self as HasIterator<'_>>::Iter { todo!() }
    fn update(&mut self) {
        self.report();
        self.modify();
    }
}

Playground.

4 Likes

Thanks a lot!! I was considering refactoring my code to remove lifetime annotation on mutable references, but this solution is more elegant and flexible!

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.