rhai/doc/src/language/eval.md
2020-09-19 12:14:02 +08:00

2.6 KiB

eval Statement

{{#include ../links.md}}

Or "How to Shoot Yourself in the Foot even Easier"

Saving the best for last, there is the ever-dreaded... eval function!

let x = 10;

fn foo(x) { x += 12; x }

let script = "let y = x;";      // build a script
script +=    "y += foo(y);";
script +=    "x + y";

let result = eval(script);      // <- look, JavaScript, we can also do this!

result == 42;

x == 10;                        // prints 10: functions call arguments are passed by value
y == 32;                        // prints 32: variables defined in 'eval' persist!

eval("{ let z = y }");          // to keep a variable local, use a statement block

print(z);                       // <- error: variable 'z' not found

"print(42)".eval();             // <- nope... method-call style doesn't work with 'eval'

Script segments passed to eval execute inside the current [Scope], so they can access and modify everything, including all variables that are visible at that position in code! It is almost as if the script segments were physically pasted in at the position of the eval call.

Cannot Define New Functions

New functions cannot be defined within an eval call, since functions can only be defined at the global level, not inside another function call!

let script = "x += 32";
let x = 10;
eval(script);                   // variable 'x' in the current scope is visible!
print(x);                       // prints 42

// The above is equivalent to:
let script = "x += 32";
let x = 10;
x += 32;
print(x);

eval is Evil

For those who subscribe to the (very sensible) motto of "eval is evil", disable eval by overloading it, probably with something that throws.

fn eval(script) { throw "eval is evil! I refuse to run " + script }

let x = eval("40 + 2");         // throws "eval is evil! I refuse to run 40 + 2"

Or overload it from Rust:

engine.register_result_fn("eval", |script: String| -> Result<(), Box<EvalAltResult>> {
    Err(format!("eval is evil! I refuse to run {}", script).into())
});

EvalPackage

There is even a package named [EvalPackage][packages] which implements the disabling override:

use rhai::Engine;
use rhai::packages::Package             // load the 'Package' trait to use packages
use rhai::packages::EvalPackage;        // the 'eval' package disables 'eval'

let mut engine = Engine::new();
let package = EvalPackage::new();       // create the package

engine.load_package(package.get());     // load the package