Passing a value with a self-related lifetime to another method

In the following reduced example:

pub struct Slice<'a> {
    pub data: &'a [u8],
}

trait Fetch {
    fn fetch(&mut self) -> Slice;
}

struct Source {
    data: Vec<u8>,
}

impl Fetch for Source {
    fn fetch(&mut self) -> Slice {
        Slice {
            data: self.data.as_slice(),
        }
    }
}

struct App {
    src: Box<dyn Fetch>,
}

impl App {
    fn new() -> Self {
        App {
            src: Box::new(Source {
                data: vec![0, 1, 2],
            }),
        }
    }

    fn f(&mut self) {
        let slice = self.src.fetch();

        self.g(slice);
    }

    fn g(&mut self, _slice: Slice) {}
}

fn main() {
    let mut app = App::new();
    app.f();
}

I get a compiler error as follows:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:38:9
   |
36 |         let slice = self.src.fetch();
   |                     ---------------- first mutable borrow occurs here
37 |
38 |         self.g(slice);
   |         ^^^^^^^-----^
   |         |      |
   |         |      first borrow later used here
   |         second mutable borrow occurs here

I think I understand what's going on here – slice lives at least as long as self, so I believe rust keeps the borrow of self from self.src.fetch() alive for as long as it needs slice. This causes us to not be able to call g() because it requires slice to be alive, but also wants to borrow self.

What I don't understand is how to fix this. Of course I could have Slice hold a Vec<u8> instead and copy the data, but I'd like to avoid that for performance reasons. In my actual code, Source returns data from a memory-mapped file that's live for as long as Source.

The other option is to inline g(), but in the real code the calling function is large already and I'm trying to make it smaller specifically by creating functions like g()!

I'd appreciate any advice!

Instead of taking the entirety of self as a parameter, make the function receive only the specific fields it needs.

2 Likes

Thank you for the quick answer! That makes total sense. It's a bit cumbersome in this case but it led to some further refactoring that makes everything better.

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.