rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.79k stars 177 forks source link

Allow to register FnMut Functions #151

Closed lammermann closed 4 years ago

lammermann commented 4 years ago

Hi, in my use case I would like to register a closure in via engine.register_fn which executes a side effect in my surrounding environment but when I try to do this I get a compile error which says:

error[E0525]: expected a closure that implements the Fn trait, but this closure only implements FnMut

Is it possible to support FnMut in register_fn? Is there a other way to archive this? Or a workaround?

Thanks very much in advance.

schungx commented 4 years ago

Check out side_effects.rs in tests.

You'll need to use Rc-wrapped interior mutable wrappers, e.g. Rc<RefCell>, to access anything that is mutable from a script.

The reason being that Engine instances can be shared and are re-entrant, so they cannot hold FnMut's.

It is better this way because you can more easily group all side-effects in a central location.

lammermann commented 4 years ago

Thanks for the very quick response. I try this out right now. It seems I'm getting livetime errors. I try to call rhai inside a function and wrapping self. However rhai seems to expect a static livetime (even though the engine itself lives only in that same function).

Is there any way I can work around this?

schungx commented 4 years ago

It expects 'static because it is re-entrant, so you can technically use one instance of Engine in a massively-parallel way.

But because of this, you have to go through some hoops for Rhai to mutate external state - it is rather dangerous if you think about it, that a text script should be allowed to mutate state outside of the scripting engine.

I suggest that you carefully design your types to make this work seamlessly. What you need is basically Rc<RefCell<T>> that allows you to keep state outside of the Engine, share a link to it via a variable into Rhai, and then use Rhai to call methods on that link. Mutability is achieved through the RefCell.

even though the engine itself lives only in that same function

Although Engine creation is cheap, you still have to deal with setting it up properly, packages, registering functions etc. So creating one Engine during each script run may not be very efficient - unless all you do is to run one single script one single time. It is much better to create a shared instance of an Engine, set it up once, then use it for all your scripts.

lammermann commented 4 years ago

Thank you for your advice. I adjusted my types to work correctly.

Going to close this now.