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

Returning Dynamic/Union Values #616

Closed makspll closed 2 years ago

makspll commented 2 years ago

I can't seem to be able to return Dynamic values from methods even though Union is a public type,

I think there should be a method of constructing those to be able to create more powerful functions

schungx commented 2 years ago

You can certainly return Dynamic from methods.

What is the error you got?

fn my_func(x: i64) -> Dynamic {
    (x + 1).into()
}

engine.register_fn("func", my_func);
makspll commented 2 years ago

Hmm, Yeah i've since realised into should be working, but it's not for some reason. Not sure why this isn't working:

    #[derive(Clone)]
    pub struct RhaiTypeRegistration(Arc<TypeRegistration>);

    pub fn get_type_by_name(&mut self, type_name: &str) -> Dynamic {
        let w = self.read();

        let registry: &TypeRegistry = w.get_resource().unwrap();

        let registry = registry.read();

        registry
            .get_with_short_name(type_name)
            .or_else(|| registry.get_with_name(type_name))
            .map(|registration| RhaiTypeRegistration::new(Arc::new(registration.clone())).into()) // <- error here
            .unwrap_or_default()
    }

I've thought maybe the type wasn't send/sync but even if i implement those it doesn't work. I get a bound error:

the trait bound `bevy_mod_scripting_rhai::rhai::ImmutableString: std::convert::From<rhai::bevy::RhaiTypeRegistration>` is not satisfied
the following other types implement trait `std::convert::From<T>`:
  <bevy_mod_scripting_rhai::rhai::ImmutableString as std::convert::From<&smartstring::SmartString<smartstring::config::LazyCompact>>>
  <bevy_mod_scripting_rhai::rhai::ImmutableString as std::convert::From<&std::string::String>>
  <bevy_mod_scripting_rhai::rhai::ImmutableString as std::convert::From<&str>>
  <bevy_mod_scripting_rhai::rhai::ImmutableString as std::convert::From<smartstring::SmartString<smartstring::config::LazyCompact>>>
  <bevy_mod_scripting_rhai::rhai::ImmutableString as std::convert::From<std::boxed::Box<str>>>
  <bevy_mod_scripting_rhai::rhai::ImmutableString as std::convert::From<std::string::String>>
required because of the requirements on the impl of `std::convert::Into<bevy_mod_scripting_rhai::rhai::ImmutableString>` for `rhai::bevy::RhaiTypeRegistration`
required because of the requirements on the impl of `std::convert::From<rhai::bevy::RhaiTypeRegistration>` for `bevy_mod_scripting_rhai::rhai::Dynamic`
1 redundant requirement hidden
required because of the requirements on the impl of `std::convert::Into<bevy_mod_scripting_rhai::rhai::Dynamic>` for `rhai::bevy::RhaiTypeRegistration`
makspll commented 2 years ago

Ah... switching into into from did the job, i didn't realise one could work without the other

schungx commented 2 years ago

Ah... switching into into from did the job, i didn't realise one could work without the other

Well, Dynamic::from is a special implementation that works with everything, not just pre-defined types. It is not the same as the From trait (although the name is the same). It is quite common in Rust to have from implementations that are separate from the From/Into traits pair.

into only works for types that are built into Rhai.

schungx commented 2 years ago

BTW, I just see your repo https://github.com/makspll/bevy_mod_scripting

Since you're supporting mlua and rhai, can you let me know how you feel about the two?

I never anticipated that Rhai can be used for gaming (since Lua is sorta the standard there).

makspll commented 2 years ago

It is not the same as the From trait

that would explain my confusion haha.

Since you're supporting mlua and rhai, can you let me know how you feel about the two? I never anticipated that Rhai can be used for gaming (since Lua is sorta the standard there).

Sure! The biggest difference between the two would be of course how workable they are with existing types, mlua, because it is built on Lua, requires a lot of traits/boilerplate to convert between things you don't own and Lua representable types. Rhai on the other hand, because it already supports ANY type, is so much more convenient to plug in.

Since my library deals with creating pass-by-ref wrappers, this interoperability is VERY valuable.

I think both languages have their strong suits, Lua's being performance of course (although the recent benchmarks I saw showed that Rhai is not that far behind!).

Rhai does work much better if you are already familiar with Rust, so having a language similar to Rust is very valuable when making a moddable game (since then your game becomes a mod itself), but a lot more people will be familiar with Lua of course.

I haven't scripted in either language for too long so I cannot comment much on language design and features, but I think Rhai is quite nice especially considering Lua indexing starting at 1 (aaaAAA), and that avoids bug-prone marshalling code (ew).

I think Overall Rhai is a fairly good contender for scripting, I don't know at the time how it would do performance wise, so it may be the case that it could only really be used for things like UI/light (and sparse) logic scripting, but even then I think it's a good option to have as part of a multi-language scripting approach

schungx commented 2 years ago

Lua indexing starting at 1

Yeah, that's always a big trip-up! Reminds me of Visual Basic days, but in VB you can further confuse everyone by using a global setting to make arrays start from zero.

makspll commented 2 years ago

Indeed, it's my one and biggest quam with Lua

Oh and I forgot but WASM support is REALLY good to have, and Rhai would allow my library to run in the browser I believe!

schungx commented 2 years ago

Rhai would allow my library to run in the browser I believe

This is correct. You're not the only one who comes to Rhai because of the lack of WASM support for Lua...

BTW, anything that you're frustrated with Rhai when integrating it into your engine? Any feature that would make your life easier?

makspll commented 2 years ago

BTW, anything that you're frustrated with Rhai when integrating it into your engine? Any feature that would make your life easier?

Hmm, native support for any references :p jk jk,

I think the integration experience is better than with Lua, I haven't encountered any deal-breaker issues yet, or anything stopping me from having the equivalent functionality I implemented in Lua.

The plugin system is quite convenient already, and avoids a lot of boilerplate which is very nice, i'll let you know if anything pops up though!

makspll commented 2 years ago

Actually @schungx, I wonder if it's possible to receive the scope as part of NativeCallContext API? I am making indexers for reflected types, and it's difficult to work around not having access to the globals available in the script

schungx commented 2 years ago

That would be difficult (otherwise it would have already been done).

That's because, for a native function, the &mut parameter can (and usually is) point to a variable in the scope. That means the whole scope is locked. Rust won't allow access to the scope within the function as long as that &mut parameter is alive.

Unless we resort to unsafe code, there is no easy way to deal with this. There is no way to guarantee that you're not gonna read the variable that happens to be passed in via the &mut parameter. We can wrap all variables in RefCell, for example, and detect data races there, but then it would have negative impacts to performance.

For functions without a &mut parameter, technically speaking you can do it, but Rhai handles both types of functions in the same way, so it is also disallowed.

makspll commented 2 years ago

Ah, yeah I see the problem, alright, I'll just have to store the state in my structs, I guess a possible solution would be having "auto-passed" arguments i.e. the function always has the first n arguments automatically pulled out of the scope by name or something, but that'd be sort of unusual

makspll commented 2 years ago

Hmm, actually constants are accessible from the native context, but there is no way to change them in between script runs, in my case the constants are "constant" per-frame, and while the script is running rather than for the entire lifetime of the script

schungx commented 2 years ago

You can set the default "tag" in the Engine, which is available for native Rust functions. This "tag" can be changed on the Engine at any time (e.g. between r uns).