Borrow held even though nothing is borrowed

⚓ Rust    📅 2026-02-10    👤 surdeus    👁️ 1      

surdeus

I have some trouble getting the borrow checker to understand my intentions. The following is a simplified version of my code: a next method tries to generate an Object (which borrows data) from a byte array at an index, advances the index into the byte array, and returns the Object. Once no more Objects can be generated (Object::get returns None), the potential rest of the array is discarded and the index is reset. But even though Object::get returned None, the borrow checker still declares the data as borrowed, and disallows the discarding:

struct Buffer {
    content: Vec<u8>,
    index: usize,
}

impl Buffer {
    // Fails to compile
    fn next(&mut self) -> Option<Object> {
        if let Some(obj) = Object::get(&self.content[self.index..]) {
            self.index += 1; // Example consumes only 1 byte, real code can consume multiple
            return Some(obj);
        }

        // `Object::get` returned `None`, so I expect nothing to be borrowed at this point.
        self.content.clear(); // error[E0502]: cannot borrow `self.content` as mutable, since it is already borrowed
        self.index = 0;
        None
    }
}

#[derive(Debug)]
struct Object<'a>(&'a u8);

impl<'a> Object<'a> {
    fn get(buffer: &'a [u8]) -> Option<Self> {
        buffer.get(0).map(|b| Self(b))
    }
}

Instead of if-let I tried other control flow constructs like match and let-else, with the same (consistent) outcome. The following two variants do compile, but have their own issues:

    // Compiles: Does not return an `Object`, no borrow checker issues.
    fn next_no_return(&mut self) {
        if let Some(obj) = Object::get(&self.content[self.index..]) {
            println!("{obj:?}");
            self.index += 1;
            return;
        }

        self.content.clear(); // no error
        self.index = 0;
    }

    // Compiles: Parses the current object 2 times, now it's OK for the borrow checker.
    fn next_parse_twice(&mut self) -> Option<Object> {
        if let Some(_obj) = Object::get(&self.content[self.offset..]) {
            let reparsed_result = Object::get(&self.content[self.offset..]);
            self.index += 1;
            return reparsed_result;
        }

        self.content.clear(); // no error
        self.index = 0;
        None
    }

How can I modify the original example to get it past the borrow checker?

4 posts - 3 participants

Read full topic

🏷️ Rust_feed