I'm trying to build a language interpreter. There is a requirement to maintain a symbol table of commands as part of the machine state. Commands themselves need to be able to add new commands to the symbol table.
What I have so far is as follows:
struct Machine<'a> {
dict: HashMap<&'a str, Box<dyn Fn(&mut Machine) -> ()>>,
}
For the interpreter function to execute a command from the symbol table, I have (simplified):
fn exec(m: &mut Machine, instr: &str) {
let f = m.dict.get(instr).unwrap();
f(m);
}
This gives me a compiler error:
error[E0502]: cannot borrow `*m` as mutable because it is also borrowed as immutable
--> src/mre.rs:9:5
|
8 | let f = m.dict.get(instr).unwrap();
| ----------------- immutable borrow occurs here
9 | f(m);
| -^^^
| |
| mutable borrow occurs here
| immutable borrow later used by call
I'm aware why this is unsafe; after all the command is allowed to remove its
own entry from the symbol table. I think the correct way forward is to somehow
clone f
before I use it, so that the borrow of m.dict
can end before the
call to f
.
Naively trying gives me:
fn exec(m: &mut Machine, instr: &str) {
let f = (*m.dict.get(instr).unwrap()).clone();
f(m);
}
error[E0599]: the method `clone` exists for struct `Box<dyn for<'r, 's> Fn(&'r mut Machine<'s>)>`, but its trait bounds were not satisfied
--> src/mre.rs:8:43
|
8 | let f = (*m.dict.get(instr).unwrap()).clone();
| ----------------------------- ^^^^^
| |
| this is a function, perhaps you wish to call it
|
::: /home/ari/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:74:1
|
74 | pub trait Fn<Args>: FnMut<Args> {
| -------------------------------
| |
| doesn't satisfy `dyn for<'r, 's> Fn(&'r mut Machine<'s>): Clone`
| doesn't satisfy `dyn for<'r, 's> Fn(&'r mut Machine<'s>): Sized`
|
::: /home/ari/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:194:1
|
194 | / pub struct Box<
195 | | T: ?Sized,
196 | | #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
197 | | >(Unique<T>, A);
| |_- doesn't satisfy `_: Clone`
|
= note: the following trait bounds were not satisfied:
`dyn for<'r, 's> Fn(&'r mut Machine<'s>): Sized`
which is required by `Box<dyn for<'r, 's> Fn(&'r mut Machine<'s>)>: Clone`
`dyn for<'r, 's> Fn(&'r mut Machine<'s>): Clone`
which is required by `Box<dyn for<'r, 's> Fn(&'r mut Machine<'s>)>: Clone`
Box<T>
implements Clone
when T
does.Closures are Clone
when certain conditions are met, so this seems like a way forward, but I'm not sure exactly how.
What's the correct way to do this?