Mutable borrow lifetime extended to the caller end of function

Hello there,

I've trying to write a set a functions that can take either a &str or a &mut Statement as parameter.

I ended up with:

  • An enum StatementRef<'r> that can hold either types,

  • The From trait is implemented for both types

  • A function that need to support either types can be implented like this:

    fn my_function<'s, S: Into<StatementRef<'s>> + 's>(statement: S) {
        let statement_ref: StatementRef = statement.into();
        match statement_ref {
            StatementRef::Str(s) => {
                // a &str
            }
            StatementRef::Statement(statement) => {
                // a &mut Statement
                statement.inc();
            }
        }
    

The code was working as expected until I tried to use the function twice in a row with a statement:

let mut statement = Statement {};
my_function(&mut statement); // ok
my_function(&mut statement); // error[E0499]: cannot borrow `statement` as mutable more than once at a time

I think this is related to the lexical lifetimes and
there's probably no work-around about it for now, but since I'm still a newbie in RUST I'm posting my example bellow so
someone can check if I didn't messed up with the lifetimes...

Example

Code also available in the playground.

pub struct Statement<'s> {
    phantom: std::marker::PhantomData<&'s ()>,
    counter: u32,
}

impl Statement<'_> {
    pub fn inc(&mut self) {
        self.counter += 1;
    }
}

// An enum that can hold either a `&str` or a `&'r mut Statement<'_>`
pub enum StatementRef<'r> {
    Str(&'r str),
    Statement(&'r mut Statement<'r>),
}

// 
// From is implemeted for both enum possible values
// 

impl<'r, 's: 'r> From<&'s str> for StatementRef<'r> {
    fn from(s: &'s str) -> Self {
        StatementRef::Str(s)
    }
}

impl<'r, 's: 'r> From<&'s mut Statement<'r>> for StatementRef<'r> {
    fn from(statement: &'s mut Statement<'r>) -> Self {
        StatementRef::Statement(statement)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn use_statement(statement: &mut Statement) {
        statement.inc();
    }

    fn use_statement_ref<'s, S: Into<StatementRef<'s>> + 's>(statement: S) {
        let statement_ref: StatementRef = statement.into();
        match statement_ref {
            StatementRef::Str(s) => {
                println!("StatementRef::Str: {}", s);
            }
            StatementRef::Statement(statement) => {
                println!("StatementRef::Statement: {}", statement.counter);
                statement.inc();
            }
        }
    }

    #[test]
    fn test() {
        let mut a_statement = Statement {
            phantom: std::marker::PhantomData,
            counter: 0,
        };

        use_statement(&mut a_statement);     // <-- mutable borrow scope limited to this function

        use_statement_ref(&mut a_statement); // <--+ mutable borrow scope extends until the end of the function
        use_statement_ref(&mut a_statement); //    |
    } // <-----------------------------------------+
}

This is the main problem: it means the Statement is borrowed forever.

You need two lifetimes to avoid that. There were also some bounds like 'x: 'y in combination with &'x Thing<'y> that requires the two lifetimes actually be the same. (Inner lifetimes must outlive outer reference lifetimes.)

This works.

(Written on mobile, can go into more detail later if desired.)

2 Likes

Thank you @quinedot! I made the changes in my real code following your indications and it worked like a charm... Now I guess I have to read the whole Learning Rust to save me time on my future troubles...

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.