Gabrbot180
Gabrbot180

Reputation: 21

Can not borrow mutable more than once at a time error when calling function in loop in Rust

I am trying to use a function that I made in rust to propose a series of choices for a user to select from and then execute an associated function from that choice. However, it is giving me a compiler error when I use it in a loop. I'm not sure why because the return value of the function is not linked to the lifetime of the reference I feel like it should be self contained. Can somebody help me refactor this.

The error that I get is

  --> src\ui\song_editor_cli.rs:15:66
   |
15 |         if let Some(res) = choice_ui::ui_offer_choices(&choices, editor) {
   |                                                        --------  ^^^^^^ `*editor` was mutably borrowed here in the previous iteration of the loop
   |                                                        |
   |                                                        first borrow used here, in later iteration of loop

my code is

pub fn ui(editor: &mut SongEditor) {
    let choices = vec![
        Choice::new("Load Song".to_string(), Box::from(load_ui)),
        Choice::new("Create Song".to_string(), Box::from(create_ui)),
        Choice::new("Edit Song".to_string(), Box::from(edit_ui)),
    ];
    println!("Hello! Welcome to Song Maker!");
    loop {
        if let Some(res) = choice_ui::ui_offer_choices(&choices, editor) {
            if let Err(err) = res {
                println!("{err}");
            }
        }
        else {
            break
        }
    }
    println!("Goodbye from Song Maker!")
}
pub struct Choice<Args, Res> {
    prompt: String,
    callback: Box<dyn Fn(Args) -> Res>
}

impl<Args, Res> Choice<Args, Res> {
    pub fn new(prompt: String, callback: Box<dyn Fn(Args) -> Res>) -> Self {
        Choice {prompt, callback}
    }
    
    pub fn call(&self, args: Args) -> Res {
        (self.callback)(args)
    }
}
pub fn ui_offer_choices<Args, Res>(choices: &Vec<Choice<Args, Res>>, args: Args) -> Option<Res> {
    for (index, choice) in choices.iter().enumerate() {
        println!("\t{}. {}", index + 1, choice.prompt)
    }
    println!("\tq. Quit");
    let mut buf = String::new();
    loop {
        buf.clear();
        io::stdin().read_line(&mut buf).expect("Failed to read user input!");
        let buf = buf.trim();
        if buf == "q" {
            return None;
        }
        let choice_number = buf.parse::<usize>().expect("failed to parse user input as number!") - 1;
        if choice_number < choices.len() {
            return Some(choices[choice_number].call(args));
        }
        else {
            println!("{} was not recognized as an available option! Try again or press 'q' to quit!", choice_number)
        }
    }
}

load_ui, create_ui, and edit_ui all have a signature like fn func_name(editor &mut SongEditor) -> Result<(), &'static str>

I have tried making the ui fn take a owned mutable SongEditor and mutably borrow it each time in the loop but that did not work. I feel like I can try changing my ui fns signature to take a Rc but I feel like I have some sort of fundamental misunderstanding of how I should be designing this and where my issue lies. In similar problems that I have seen on stack overflow there is typically a return value with a lifetime tied to the borrowed value. I do not think that that exists in my use case as my only returned lifetime is static.

Upvotes: 2

Views: 56

Answers (1)

kmdreko
kmdreko

Reputation: 60493

I don't think you can make this work as-is. Args can only be deduced to a singular type which for mutable references will include the lifetime. So you have choices with a singular lifetime for the parameter its expecting which must be the same as editor. Thus in your loop you call ui_offer_choices the same lifetime each time, which is not allowed.

You probably need to change Choice to use dyn Fn(&mut Args) -> Res instead so that the lifetime can be deferred until the call-site. So change all Args to &mut Args like shown here on the playground.

Upvotes: 3

Related Questions