Reputation: 21
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
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