youchoose
youchoose copied to clipboard
Using a FnMut closure with Preview
Hey, Rust newbie here. I ran into a problem using the Preview feature, and couldn't figure out what the correct way to handle it was. I'm probably not thinking about approaching what I want to do correctly, so any feedback is appreciated.
I'm trying to get the Preview to display the output from a rand
random generator, which needs mut
access.
Here's the code itself. I tried making r
a function that would return the closure that I intend to run with the mut rng
moved into scope, but that changes the type and prevents it from running.
fn main() {
let opt = Opts::parse();
let mut rng: Pcg64 = Seeder::from(opt.seed).make_rng();
let r = move |rng: Pcg64| {
|dice| -> String {
let r: u16 = rng.gen_range(1..=get_upper_bound_of_dice(dice));
r.to_string()
}
};
let mut menu = youchoose::Menu::new(Dice::iter()).preview(r(rng));
menu.show();
}
I'm looking at the youchoose code now to see if I can make the typing on .preview
more permissive, but if I'm doing something conceptually wrong with my approach, I'd love to know. Thanks!
Adding the error:
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
--> src/main.rs:66:9
|
66 | |dice| -> String {
| ^^^^^^^^^^^^^^^^ this closure implements `FnMut`, not `Fn`
67 | let r: u16 = rng.gen_range(1..=get_upper_bound_of_dice(dice));
| --- closure is `FnMut` because it mutates the variable `rng` here
...
73 | let mut menu = youchoose::Menu::new(Dice::iter()).preview(r(rng));
| ------- the requirement to implement `Fn` derives from here
I was able to pass my function through by changing the type expected from Fn to FnMut, but I doubt that's a stable solution.
diff --git a/src/lib.rs b/src/lib.rs
index 11dc59d..636d276 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -195,8 +195,8 @@ where
if let Some(item) = self.iter.next() {
let mut new_item =
Item::new(&item, self.item_icon, self.chosen_item_icon);
- if let Some(preview) = &self.preview {
- new_item.preview(item, &preview.func);
+ if let Some(preview) = &mut self.preview {
+ new_item.preview(item, &mut preview.func);
}
self.state.items.push(new_item);
} else {
@@ -322,7 +322,7 @@ where
/// String.
pub fn preview<F>(mut self, func: F) -> Menu<'a, I, D>
where
- F: Fn(D) -> String + 'static,
+ F: FnMut(D) -> String + 'static,
{
let func = DispFunc::new(Box::new(func));
self.screen.set_pos(ScreenSide::Left, 0.5);
@@ -704,7 +704,7 @@ impl<'a> Item<'a> {
&self.repr
}
- fn preview<D: fmt::Display>(&mut self, thing: D, func: &DispFunc<D>) {
+ fn preview<D: fmt::Display>(&mut self, thing: D, func: &mut DispFunc<D>) {
self.preview = Some(func.eval(thing));
}
}
@@ -817,17 +817,17 @@ struct DispFunc<D>
where
D: fmt::Display,
{
- func: Box<dyn Fn(D) -> String>,
+ func: Box<dyn FnMut(D) -> String>,
}
impl<D> DispFunc<D>
where
D: fmt::Display,
{
- fn new(func: Box<dyn Fn(D) -> String>) -> DispFunc<D> {
+ fn new(func: Box<dyn FnMut(D) -> String>) -> DispFunc<D> {
DispFunc { func }
}
- fn eval(&self, param: D) -> String {
+ fn eval(&mut self, param: D) -> String {
(*self.func)(param)
}
}