rhaiscript / rhai

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

Array support in scope #782

Closed rikvdkleij closed 9 months ago

rikvdkleij commented 9 months ago

Adding a Vec<String> or array to a scope doesn't seems to be recognized as a Rhai array during evaluation of a script.

How can a Rust Vec be added to the scope as an Rhai array?

schungx commented 9 months ago

You can convert it first into an Array by making it a Dynamic.

let v : Dynamic = value.into();
rikvdkleij commented 9 months ago

Thanks for your quick reply.

Ok, that was not clear from the documentation: https://rhai.rs/book/language/arrays.html#arrays

The Rust type of a Rhai array is rhai::Array which is an alias to Vec<Dynamic>.

So the documentation states the elements have to be Dynamic value.

Unfortunately, it still not works:

                let codes: Vec<String> = ....;
                let foo: Dynamic = codes.into();
                scope.push_constant("bar", foo);

Script contains:

bar.contains("x")

Error:

ErrorFunctionNotFound", error_message: "Function not found: contains (array, &str | ImmutableString | String)
schungx commented 9 months ago

Hhhmmm... It properly recognizes the type as array. So the data is correct.

Just the function is not found, which is strange.

Can you try: "x" in bar

rikvdkleij commented 9 months ago

Can you try: "x" in bar

Gives the same error.

 "Function not found: contains (array, &str | ImmutableString | String) 

AFAICS the Rhai runtime still "interpretes" bar as a Rust type.

rikvdkleij commented 9 months ago

O wait, type_of() now gives array for bar.

That differs with my previous attempts.

schungx commented 9 months ago

O wait, type_of() now gives array for bar.

That differs with my previous attempts.

Yes, that's the point. The data type is correct.

Just the function is not found.

You are not using new_raw by any chance?

Or can you share a reproducible snippet?

rikvdkleij commented 9 months ago

You are not using new_raw by any chance?

Yes, an engine is created with Engine::new_raw() because of safety reasons and side-effects as printing should not be possible.

So it looks like that to solve this the BasicArrayPackage package has to be registered in the engine.

That's confusing because by using the raw engine (and so only the core package) arrays are supported but their functions not....

But I'm glad it's now clarified. Thanks for your help!

schungx commented 9 months ago

Yes, an engine is created with Engine::new_raw() because of safety reasons and side-effects as printing should not be possible.

Actually, raw engines do not have anything to do with security and safety. You can always disable printing etc. by calling on_print and on_debug and put in stubs. You can also disable module resolution by setting it to an empty resolver. All these you can do on a standard Engine.

Raw engines are for environments with limited memory, so they don't load the entire standard library, and you need to pick-and-choose the libraries you need.

For your concerns you may consider:

// Create the new scripting Engine
let mut engine = Self::new();
// Disable stuff with side effects
engine
    .set_module_resolver(rhai::module::resolvers::DummyModuleResolver::new())
    .on_print(|_| {})
    .on_debug(|_, _, _| {});
// Make engine immutable
let engine = engine;
rikvdkleij commented 9 months ago

Actually, raw engines do not have anything to do with security and safety. You can always disable printing etc. by calling on_print and on_debug and put in stubs. You can also disable module resolution by setting it to an empty resolver. All these you can do on a standard Engine.

Okay, but safety starts with disabling everything and only enable what you need :smile: