rustiterationlifetime

Access mutable borrows in a default trait implementation


I want to access and manipulate active_outer_index_iterator and current_outer_index within trait ISequentialOuterIndexRemoverV1. They will be used in function load_outer_index() which is a default trait implementation.

Since this trait will also be applied on another struct with different inner fields, I am not implementing load_outer_index() directly on SequentialOuterIndexRemover.

/// Helper to sequentially read and remove outer indices from the index list
/// in slot storage
pub struct SequentialOuterIndexRemover<'a> {
    /// Iterator to read active outer indices from index list
    pub active_outer_index_iterator: ActiveOuterIndexIteratorV2<'a>,

    /// The currently read outer index
    pub current_outer_index: Option<OuterIndex>,
}

pub trait ISequentialOuterIndexRemoverV1<'a> {
    fn active_outer_index_iterator(&'a mut self) -> &'a mut ActiveOuterIndexIteratorV2;

    fn current_outer_index(&mut self) -> &mut Option<OuterIndex>;
}

So far I tried two solutions:

Case 1

I added load_outer_index() to the trait and tried to access active_outer_index_iterator() and current_outer_index(). I am getting error 'cannot borrow *self as mutable more than once at a time'.



pub trait ISequentialOuterIndexRemoverV1<'a> {
    fn active_outer_index_iterator(&'a mut self) -> &'a mut ActiveOuterIndexIteratorV2;

    fn current_outer_index(&mut self) -> &mut Option<OuterIndex>;

    // Error
    // rustc: cannot borrow `*self` as mutable more than once at a time
    // second mutable borrow occurs here
    fn load_outer_index(&'a mut self, ctx: &mut ArbContext) {
        *self.current_outer_index() = self.active_outer_index_iterator().next(ctx);
    }
}

Case 2

I tried the solution from https://stackoverflow.com/a/26194992/27713386. I removed <'a> from top of the trait and added it to the function. The trait compiles but now I am unable to write implementation for active_outer_index_iterator(). I am puzzled with the compiler error because both signatures seem to be the same.

rustc: method not compatible with trait
expected signature `fn(&'a mut SequentialOuterIndexRemover<'_>) -> &'a mut ActiveOuterIndexIteratorV2<'a>`
   found signature `fn(&'a mut SequentialOuterIndexRemover<'_>) -> &'a mut ActiveOuterIndexIteratorV2<'a>`
pub trait ISequentialOuterIndexRemover {
    // <'a> removed from above and added to this function
    fn active_outer_index_iterator<'a>(&'a mut self) -> &mut ActiveOuterIndexIteratorV2;

    fn current_outer_index(&mut self) -> &mut Option<OuterIndex>;


    // Works now
    fn load_outer_index<'a>(&'a mut self, ctx: &mut ArbContext) {
        *self.current_outer_index() = self.active_outer_index_iterator().next(ctx);
    }
}

impl<'a> ISequentialOuterIndexRemover for SequentialOuterIndexRemover<'a> {
    // rustc: method not compatible with trait
    // expected signature `fn(&'a mut SequentialOuterIndexRemover<'_>) -> &'a mut ActiveOuterIndexIteratorV2<'a>`
    //    found signature `fn(&'a mut SequentialOuterIndexRemover<'_>) -> &'a mut ActiveOuterIndexIteratorV2<'a>`
    fn active_outer_index_iterator<'a>(&'a mut self) -> &mut ActiveOuterIndexIteratorV2 {
        &mut self.active_outer_index_iterator
    }

    fn current_outer_index(&mut self) -> &mut Option<OuterIndex> {
        &mut self.current_outer_index
    }
}

Edit 1

As pointed by Jmb, I forgot to add lifetime <'a> on the method when posting the question. The issue persists even after editing.

Edit 2- minimum reproducible example

struct InnerStruct<'a> {
    data: &'a mut u8,
}

struct MyStruct<'a> {
    inner: InnerStruct<'a>,
    cache: u8,
}

trait GetInnerMut<'a> {
    fn get_inner_mut(&'a mut self) -> &mut InnerStruct;

    fn get_cache(&mut self) -> &mut u8;

    fn process(&'a mut self) {
        let inner_mut = self.get_inner_mut();
        // rustc: cannot borrow `*self` as mutable more than once at a time
        // second mutable borrow occurs here
        *self.get_cache() = *inner_mut.data;
    }
}

impl<'a> GetInnerMut<'a> for MyStruct<'a> {
    fn get_inner_mut(&'a mut self) -> &mut InnerStruct {
        &mut self.inner
    }

    fn get_cache(&mut self) -> &mut u8 {
        &mut self.cache
    }
}

Solution

  • You stuck the 'a lifetime in the wrong spot. It parameterizes InnerStruct, not &mut self. You can kind of see this if you peel back the layers: the lifetime of &mut self.inner is obviously the same as that of &mut self, but the data contained therein has the separate lifetime of 'a.

    struct InnerStruct<'a> {
        data: &'a mut u8,
    }
    
    struct MyStruct<'a> {
        inner: InnerStruct<'a>,
        cache: u8,
    }
    
    trait GetInnerMut<'a> {
        fn get_inner_mut(&mut self) -> &mut InnerStruct<'a>;
    }
    
    impl<'a> GetInnerMut<'a> for MyStruct<'a> {
        fn get_inner_mut(&mut self) -> &mut InnerStruct<'a> {
            &mut self.inner
        }
    }