Double borrow with getter method

I'm feeling very stupid right now.

This is the code snippet. I just wanted to add a getter method instead of accessing directly the rl field.

struct Ctx {
    rl: rustyline::DefaultEditor,
}

impl Ctx {
    fn get_mut_rl(&mut self) -> &mut rustyline::DefaultEditor {
        &mut self.rl
    }
}

fn do_something(_ctx: &mut Ctx, _line: &str) {
    todo!()
}

fn main() {
    let mut ctx = Ctx {
        rl: rustyline::DefaultEditor::new().unwrap(),
    };

    let rlp = ctx.get_mut_rl();

    loop {
        // This is not working
        // let readline = rlp.readline(">> ");

        // This is working
        let readline = ctx.rl.readline(">> ");

        match readline {
            Ok(line) if !line.is_empty() => {
                do_something(&mut ctx, &line);
            }
            Err(err) => {
                eprintln!("Error: {:?}", err);
                break;
            }
            _ => {}
        }
    }
}

If I use:

let readline = rlp.readline(">> ");

instead of:

let readline = ctx.rl.readline(">> ");

the compiler will complain with:

error[E0499]: cannot borrow `ctx` as mutable more than once at a time
  --> src/main.rs:31:30
   |
20 |     let rlp = ctx.get_mut_rl();
   |               --- first mutable borrow occurs here
...
24 |         let readline = rlp.readline(">> ");
   |                        --- first borrow later used here
...
31 |                 do_something(&mut ctx, &line);
   |                              ^^^^^^^^ second mutable borrow occurs here

But what is the difference between the two implementations?

The answer is right there in the error message: cannot borrow ctx as mutable more than once at a time.

When you bind rlp, it holds a mutable (exclusive!) reference to ctx. This means that for the duration of rlp's lifetime ctx cannot be accessed.When you don't use rlp the compiler is clever enough to shorten its lifetime automatically so it doesn't live inside the loop (through NLL).

2 Likes

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.