Unable to create function in static struct

Hi, I'm still pretty new to Rust and I've been fighting the borrow-checker for about two solid days now on this.

I'm writing an 8086 Emulator and want to keep all the info about each instruction and its constants close together.
Thus, I had the idea of putting each instruction execution into a closure that I can call upon, whenever that specific instruction comes up.
I come from Java, where this is just a lambda for a functional interface.

I did get it to work by just passing a function pointer to a function declared elsewhere, but I want to avoid that at all costs, since I don't want to split the information about each instruction and its data.

(Both the current fields of the Instruction and the arguments to the closure are not final, yet)

pub(crate) struct Instruction {
    pub code: u8,
    pub name: &'static str,
    pub args: [&'static str; 2],
    pub execute: &'static Executor,
}

type Executor = dyn FnMut(&mut Computer) -> &mut Computer;

pub(crate) static INSTRUCTIONS: &'static [Instruction] = &[
    Instruction { code: 0x00, name: "ADD", args: ["Eb", "Gb"], execute: &|computer| { computer } },
    Instruction { code: 0x01, name: "ADD", args: ["Ev", "Gv"], execute: &|computer| { computer } },

So my question is: Is there any way to make this work without splitting info about the Instruction too far from the code for it?

Because if not, then I don think either me or the language are not ready for one another and I'm just gonna move back to Java again.

I just changed this on a whim and suddenly, it compiles and runs.

type Executor = dyn (FnMut(&mut Computer) -> &mut Computer) + Sync;

I feel very dumb right now.
Should I delete this?

Nevermind, apparently to actually be able to call the function, it has to be mutably borrowable.
But 'static fields can't be mutable, so I can't do that.

Here is the error I'm stuck at now:

error[E0596]: cannot borrow `*instruction.execute` as mutable, as it is behind a `&` reference
  --> src/main.rs:37:14
   |
37 |         c =  (instruction.execute)(c);
   |              ^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
   | 
  ::: src/emux8086/mod.rs:62:18
   |
62 |     pub execute: &'static Executor,
   |                  ----------------- help: consider changing this to be mutable: `&'static mut Executor`

Can someone point me in the right direction?
I have the feeling that Rust really doesn't want me to do it in this way.

Do you really need these instructions to be able to capture environment? Since they are static, and you said you used function items previously, I'd expect the answer to be no. In this case, you could just continue storing function pointers instead of arbitrarily general, full-fledged closures:

pub(crate) struct Instruction {
    pub code: u8,
    pub name: &'static str,
    pub args: [&'static str; 2],
    pub execute: Executor,
}

type Executor = fn(&mut Computer) -> &mut Computer;


pub(crate) static INSTRUCTIONS: &[Instruction] = &[
    Instruction { code: 0x00, name: "ADD", args: ["Eb", "Gb"], execute: |computer| { computer } },
    Instruction { code: 0x01, name: "ADD", args: ["Ev", "Gv"], execute: |computer| { computer } },
];

(Incidentally, an API design question: it looks very strange to me that you allow the Computer to be mutated yet you force it to be returned from the closure as well. Wouldn't allowing exactly one of these suffice? If you allow mutation through a reference, you can already observe it without returning it. If you want a more functional, immutable style, you could just pass the previous state by value or immutable reference and return a modified version of it by value.)

2 Likes

Thank you for your help!
I did not know that closures behaved that way. I still have a lot to learn.

To your API question: The &mut Computer argument is left over from when I wanted to make the whole design immutable and was moving to an all-mutable design, when I encountered the problem with the function pointers.
Nothing of this is really final yet, since I'm still learning the x86 architecture myself.

If it's of any help, I'm doing something broadly similar and this is what I came up with:

pub struct SingleArgFunction {
    pub name: &'static str,
    pub fun: fn(f64) -> f64,
    pub inv: Option<fn(r: &Range) -> Option<Range>>,
}
pub static ONE_ARG_FUNCTIONS: [SingleArgFunction; 13] = [
    SingleArgFunction { name: "sqrt", fun: sqrt, inv: Some(sqrt_rev) },
    SingleArgFunction { name: "square", fun: square, inv: Some(square_rev) },
    SingleArgFunction { name: "log", fun: log, inv: None },
    SingleArgFunction { name: "reciprocal", fun: reciprocal, inv: Some(reciprocal_rev) },
    SingleArgFunction { name: "abs", fun: abs, inv: Some(abs_rev) },
    SingleArgFunction { name: "inc", fun: inc, inv: Some(inc_rev) },
    SingleArgFunction { name: "dec", fun: dec, inv: Some(dec_rev) },
    SingleArgFunction { name: "to_one", fun: to_one, inv: None },
    SingleArgFunction { name: "neg", fun: neg, inv: Some(neg_rev) },
    SingleArgFunction { name: "tau_sigmoid", fun: tau_sigmoid, inv: None },
    SingleArgFunction { name: "tang_hyper", fun: tang_hyper, inv: None },
    SingleArgFunction { name: "relu", fun: relu, inv: None },
    SingleArgFunction { name: "sine", fun: sine, inv: Some(sine_rev) },
];