rhaiscript / rhai

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

How to pass a callback to a registered type function? #465

Closed RazrFalcon closed 3 years ago

RazrFalcon commented 3 years ago

I saw examples of passing a callback from Rhai to Rust in the book, but they use global functions. I'm trying to implement something like this:

variable.do_stuff(|v| println(v));
schungx commented 3 years ago

Not very sure what you're looking for. There are multiple ways to call Rhai functions from Rust (consult the Book).

Depends on what you want to achieve. For example, you seem to be using Rhai scripts are event handlers (aka callbacks), and there is a usage pattern in the Book on this.

RazrFalcon commented 3 years ago

I'm basically looking for a way to write:

impl MyType {
    fn do_stuff(&mut self, callback: rhai::FnPtr) {
        callback.call_dynamic(); // where can I get `NativeCallContext` from?
    }
}
schungx commented 3 years ago

Try:

https://rhai.rs/book/rust/context.html https://rhai.rs/book/language/fn-ptr.html#nativecallcontext

RazrFalcon commented 3 years ago

Yes, I read the book. The problem is that I don't have an access to the engine from the type. And storing a copy of the engine (if this even possible) in each registered type seems like an overkill.

It seems like the only way is to have a global function and pass a type and a closure into it.

schungx commented 3 years ago

What's wrong with:

impl MyType {
    fn do_stuff(context: NativeCallContext, &mut self, callback: rhai::FnPtr) {
        callback.call_dynamic(context, ...);
    }
}
RazrFalcon commented 3 years ago

self must be the first argument. And it would not compile anyway:

error[E0277]: the trait bound `for<'r, 's> fn(&'r mut TestStruct, NativeCallContext<'s>, FnPtr) {TestStruct::do_stuff}: RegisterNativeFunction<_, ()>` is not satisfied
   --> src/lib.rs:562:34
    |
562 |         .register_fn("do_stuff", TestStruct::do_stuff)
    |                                  ^^^^^^^^^^^^^^^^^^^^ the trait `RegisterNativeFunction<_, ()>` is not implemented for `for<'r, 's> fn(&'r mut TestStruct, NativeCallContext<'s>, FnPtr) {TestStruct::do_stuff}`
schungx commented 3 years ago
fn do_stuff(context: NativeCallContext, obj: &mut MyType, callback: rhai::FnPtr) {
    callback.call_dynamic(context, ...);
}

engine.register_fn("do_stuff", do_stuff);
RazrFalcon commented 3 years ago

Thanks! This is what I was looking for.

schungx commented 3 years ago

Or simpler:

impl MyType {
    fn do_stuff(&mut self, context: NativeCallContext, callback: rhai::FnPtr) {
        callback.call_dynamic(context, ...);
    }
}

engine.register_fn("do_stuff", |context: NativeCallContext, obj: &mut MyType, callback: FnPtr| obj.do_stuff(context, callback));

However, if you have a context parameter, you may not really want that method into MyType in the first place...