### Install rhai-run tool Source: https://github.com/rhaiscript/rhai/blob/main/scripts/README.md Installs the rhai-run binary from the local source path. ```sh cargo install --path . --bin rhai-run ``` -------------------------------- ### Run Rhai Examples Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Command to run the examples provided with the Rhai distribution. Replace `{example_name}` with the specific example you want to execute. ```sh cargo run --example {example_name} ``` -------------------------------- ### Install Rhai tools Source: https://github.com/rhaiscript/rhai/blob/main/src/bin/README.md Installs Rhai binaries from the current path with all features enabled. ```sh cargo install --path . --bins --features bin-features ``` ```sh cargo install --path . --bin sample_app_to_run --features bin-features ``` -------------------------------- ### Evaluate Rhai Expression Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md A simple example that evaluates an expression and prints the result. No special setup is required. ```rust use rhai::Engine; fn main() { let mut engine = Engine::new(); let result = engine.eval("1 + 2 * 3").unwrap(); println!("Result: {}", result); } ``` -------------------------------- ### Serialize and Deserialize Rust Types with Serde Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md An example demonstrating how to serialize and deserialize Rust types using the `serde` crate within Rhai scripts. This requires enabling the `serde` feature for Rhai. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; use serde::{Serialize, Deserialize}; #[derive(Debug, Clone, Serialize, Deserialize)] struct MyData { name: String, value: i32, } fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register the Rust type for serialization/deserialization engine.register_type::(); // Create a MyData instance in Rust let rust_data = MyData { name: "example".to_string(), value: 42 }; scope.push("rust_data", rust_data.clone()); // Serialize the Rust data within the script let serialized_data = engine.eval_with_scope::(&mut scope, "rust_data.serialize() as String").unwrap(); println!("Serialized data: {}", serialized_data); // Deserialize the data back into a Rhai object let deserialized_rhai_obj = engine.eval_with_scope::(&mut scope, &format!("MyData::deserialize(\"{}\")", serialized_data)).unwrap(); scope.push("deserialized_data", deserialized_rhai_obj); // Access fields of the deserialized object let name = engine.eval_with_scope::(&mut scope, "deserialized_data.name").unwrap(); let value = engine.eval_with_scope::(&mut scope, "deserialized_data.value").unwrap(); println!("Deserialized name: {}, value: {}", name, value); // Verify by deserializing back to Rust struct let deserialized_rust_data = engine.eval_with_scope::(&mut scope, "deserialized_data.deserialize() as MyData").unwrap(); assert_eq!(rust_data, deserialized_rust_data); println!("Successfully round-tripped data."); } ``` -------------------------------- ### Execute Rhai scripts Source: https://github.com/rhaiscript/rhai/blob/main/scripts/README.md Run a specific Rhai script file using either the installed binary or cargo run. ```sh rhai-run ./scripts/test_script_to_run.rhai ``` ```sh cargo run --bin rhai-run ./scripts/test_script_to_run.rhai ``` -------------------------------- ### Build the project with a specific profile Source: https://github.com/rhaiscript/rhai/blob/main/no_std/no_std_test/README.md Compile the project using one of the defined profiles: unix, windows, or macos. ```bash cargo +nightly build --profile unix ``` -------------------------------- ### Build the project with nightly compiler Source: https://github.com/rhaiscript/rhai/blob/main/no_std/no_std_test/README.md Use the nightly toolchain to compile the project in release mode. ```bash cargo +nightly build --release ``` -------------------------------- ### Create Rhai Engine and Evaluate Scripts Source: https://context7.com/rhaiscript/rhai/llms.txt Demonstrates creating a Rhai engine and evaluating various script types like print statements, arithmetic expressions, and string concatenations. Ensure the correct return type is specified for `eval`. ```rust use rhai::{Engine, EvalAltResult}; fn main() -> Result<(), Box> { // Create a new scripting engine let engine = Engine::new(); // Run a script that prints output (returns ()) engine.run(r#"print(\"hello, world!\")"#)?; // Evaluate an expression and get the result let result = engine.eval::("40 + 2")?; println!("The Answer: {result}"); // prints 42 // Evaluate floating-point expressions let float_result = engine.eval::("3.14159 * 2.0")?; println!("Pi times 2: {float_result}"); // Evaluate string expressions let greeting = engine.eval::(r#"\"Hello, \" + \"Rhai!\""#)?; println!("{greeting}"); // prints "Hello, Rhai!" // Evaluate boolean expressions let is_valid = engine.eval::("10 > 5 && 3 < 7")?; println!("Is valid: {is_valid}"); // prints true Ok(()) } ``` -------------------------------- ### Generate Rhai Language Server Definition Files Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Shows how to generate definition files for use with the Rhai Language Server. This requires enabling the `metadata` feature for the Rhai crate. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register a custom type and function engine.register_type::() .register_fn("new", MyStruct::new) .register_get("data", MyStruct::get_data) .register_set("data", MyStruct::set_data) .register_fn("add", MyStruct::add); engine.register_fn("hello", || "world"); // Generate definition file let def_content = engine.generate_doc_files().unwrap(); println!("Generated definition file content:\n{}", def_content); } #[derive(Debug, Clone, Default)] struct MyStruct { data: i32, } impl MyStruct { fn new() -> Self { MyStruct { data: 0 } } fn get_data(&self) -> i32 { self.data } fn set_data(&mut self, value: i32) { self.data = value; } fn add(&mut self, value: i32) { self.data += value; } } ``` -------------------------------- ### Run Rhai Benchmarks Source: https://github.com/rhaiscript/rhai/blob/main/benches/README.md Execute the micro benchmarks for Rhai using Cargo. Ensure you are using the nightly toolchain. ```bash cargo +nightly bench ``` -------------------------------- ### Rhai Script Language Basics Source: https://context7.com/rhaiscript/rhai/llms.txt Demonstrates basic Rhai syntax for variables, types, string interpolation, arrays, object maps, control flow, loops, functions, closures, array methods, and error handling. ```rhai // Variables and constants let x = 42; // mutable variable const PI = 3.14159; // constant (cannot be modified) // Basic types let integer = 42; let float = 3.14; let boolean = true; let string = "hello"; let char = 'A'; let unit = (); // unit type (like void) // String interpolation let name = "World"; print(`Hello, ${name}!`); // template strings with backticks // Arrays let arr = [1, 2, 3, 4, 5]; arr.push(6); let first = arr[0]; let len = arr.len(); // Object maps let obj = #{ name: "John", age: 30, active: true }; obj.email = "john@example.com"; // add new property print(obj.name); // Control flow if x > 0 { print("positive"); } else if x < 0 { print("negative"); } else { print("zero"); } // Switch statement let result = switch x { 1 => "one", 2 => "two", 3..=10 => "small", _ => "other" }; // Loops for i in 0..10 { print(i); } let i = 0; while i < 5 { i += 1; } loop { if condition { break; } } // Functions fn add(a, b) { a + b // last expression is return value } fn factorial(n) { if n <= 1 { return 1; // explicit return } n * factorial(n - 1) } // Closures let multiply = |x, y| x * y; let result = multiply(6, 7); // Array methods let numbers = [1, 2, 3, 4, 5]; let doubled = numbers.map(|x| x * 2); let evens = numbers.filter(|x| x % 2 == 0); let sum = numbers.reduce(|acc, x| acc + x, 0); // Error handling try { throw "Something went wrong!"; } catch (err) { print(`Error: ${err}`); } ``` -------------------------------- ### Registering Static Modules in Rhai Source: https://context7.com/rhaiscript/rhai/llms.txt Organize functions into namespaces by creating and registering modules. Functions can be accessed via their namespace, e.g., `math::square(5)`. ```rust use rhai::{Engine, Module, EvalAltResult, Shared}; fn main() -> Result<(), Box> { let mut engine = Engine::new(); // Create a module let mut math_module = Module::new(); // Add functions to the module math_module.set_native_fn("square", |x: i64| Ok(x * x)); math_module.set_native_fn("cube", |x: i64| Ok(x * x * x)); math_module.set_native_fn("abs", |x: i64| Ok(x.abs())); // Convert to shared module let math_module: Shared = math_module.into(); // Register as a static module with namespace engine.register_static_module("math", math_module.clone()); // Can also register nested namespaces engine.register_static_module("utils::math", math_module); // Use the module in scripts let result = engine.eval::(r#" let a = math::square(5); // 25 let b = math::cube(3); // 27 let c = utils::math::abs(-10); // 10 a + b + c // 62 "#)?; println!("Result: {result}"); // prints 62 // Register a global module (functions available without namespace) let mut global_funcs = Module::new(); global_funcs.set_native_fn("double", |x: i64| Ok(x * 2)); engine.register_global_module(global_funcs.into()); let doubled = engine.eval::("double(21)")?; assert_eq!(doubled, 42); Ok(()) } ``` -------------------------------- ### Run a Rhai application Source: https://github.com/rhaiscript/rhai/blob/main/src/bin/README.md Executes a specific binary with the bin-features feature set enabled. ```sh cargo run --features bin-features --bin sample_app_to_run ``` -------------------------------- ### Register Rust Functions with String Arguments Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Demonstrates different ways to register Rust functions that accept string arguments in Rhai scripts. Covers handling string slices and owned strings. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; // Function taking a string slice fn process_str_slice(s: &str) -> String { format!("Processed slice: '{}'", s.to_uppercase()) } // Function taking an owned String fn process_string(s: String) -> String { format!("Processed String: '{}'", s.to_lowercase()) } fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register functions engine.register_fn("process_slice", process_str_slice); engine.register_fn("process_string", process_string); // Call functions from script let result1 = engine.eval_with_scope::(&mut scope, "process_slice(\"Hello World\")").unwrap(); println!("Result 1: {}", result1); let result2 = engine.eval_with_scope::(&mut scope, "process_string(\"Another Test\")").unwrap(); println!("Result 2: {}", result2); } ``` -------------------------------- ### Register and Call Rhai Closure Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Demonstrates how to store a Rhai closure and call it later within Rust. Ensure the Rhai engine is configured to handle closures. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register a closure engine.register_fn("my_closure", || 42); // Call the closure let result = engine.call_fn::( &mut scope, "my_closure", ()) .unwrap(); println!("Closure result: {}", result); } ``` -------------------------------- ### Run Rhai Engine Tests Source: https://github.com/rhaiscript/rhai/blob/main/tests/README.md Execute the Rhai engine tests using Cargo. Ensure the 'bin-features' are enabled. ```bash cargo test --features bin-features ``` -------------------------------- ### Register a Simple Rust Function Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Shows the basic process of registering a simple Rust function to be callable from Rhai scripts. No complex types or closures are involved. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; fn greet(name: &str) -> String { format!("Hello, {}!", name) } fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register the Rust function 'greet' engine.register_fn("greet", greet); // Call the registered function from a script let result = engine.eval_with_scope::(&mut scope, "greet('Rhai')").unwrap(); println!("Script output: {}", result); } ``` -------------------------------- ### Generate Keyword Table with gperf Source: https://github.com/rhaiscript/rhai/blob/main/tools/keywords.txt Use gperf to generate the keyword table from the provided input file. The output will be in ANSI-C format and requires manual editing for Rust integration. ```bash gperf keywords.txt ``` -------------------------------- ### Register Native Rust Functions with Rhai Engine Source: https://context7.com/rhaiscript/rhai/llms.txt Shows how to register Rust functions and closures with the Rhai engine, making them callable from scripts. Functions can accept clonable parameters and return clonable types. Closures can also be registered directly. ```rust use rhai::{Engine, EvalAltResult}; // Define a native Rust function fn add(x: i64, y: i64) -> i64 { x + y } // Function that works with strings fn greet(name: &str) -> String { format!("Hello, {}!", name) } // Function that modifies its first argument fn increment(value: &mut i64) { *value += 1; } fn main() -> Result<(), Box> { let mut engine = Engine::new(); // Register functions with the engine engine.register_fn("add", add); engine.register_fn("greet", greet); engine.register_fn("increment", increment); // Also register closures directly engine.register_fn("multiply", |x: i64, y: i64| x * y); engine.register_fn("is_even", |x: i64| x % 2 == 0); // Call registered functions from scripts let result = engine.eval::("add(40, 2)")?; assert_eq!(result, 42); let greeting = engine.eval::(r#"greet(\"World\")"#)?; assert_eq!(greeting, "Hello, World!"); let product = engine.eval::("multiply(6, 7)")?; assert_eq!(product, 42); let even_check = engine.eval::("is_even(42)")?; assert!(even_check); Ok(()) } ``` -------------------------------- ### Register Rust Type with Methods and Getters/Setters Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Shows how to register a Rust type and its methods, getters, and setters for use within Rhai scripts. This allows seamless interaction between Rust and Rhai objects. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; #[derive(Debug, Clone, Default)] struct MyStruct { data: i32, } impl MyStruct { fn new() -> Self { MyStruct { data: 0 } } fn get_data(&self) -> i32 { self.data } fn set_data(&mut self, value: i32) { self.data = value; } fn add(&mut self, value: i32) { self.data += value; } } fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register the Rust type and its methods/getters/setters engine.register_type::() .register_fn("new", MyStruct::new) .register_get("data", MyStruct::get_data) .register_set("data", MyStruct::set_data) .register_fn("add", MyStruct::add); // Use the registered type in a script let ast = engine.compile("let x = new(); x.data = 10; x.add(5); x.data").unwrap(); let result = engine.eval_ast_with_scope::(&mut scope, &ast).unwrap(); println!("Script result: {}", result); } ``` -------------------------------- ### Compile Scripts to AST Source: https://context7.com/rhaiscript/rhai/llms.txt Compiles Rhai scripts into an AST for efficient repeated evaluation. Supports scope-based constant propagation for optimization. ```rust use rhai::{Engine, Scope, EvalAltResult, AST}; fn main() -> Result<(), Box> { let engine = Engine::new(); // Compile a script to an AST let ast: AST = engine.compile("40 + 2")?; // Evaluate the AST multiple times efficiently for _ in 0..100 { let result = engine.eval_ast::(&ast)?; assert_eq!(result, 42); } // Compile a more complex script with functions let ast = engine.compile(r#" fn factorial(n) { if n <= 1 { 1 } else { n * factorial(n - 1) } } factorial(10) "#)?; let result = engine.eval_ast::(&ast)?; println!("10! = {result}"); // prints 3628800 // Compile with scope for constant propagation (optimization) let mut scope = Scope::new(); scope.push_constant("MULTIPLIER", 10_i64); let ast = engine.compile_with_scope(&scope, "x * MULTIPLIER")?; scope.push("x", 5_i64); let result = engine.eval_ast_with_scope::(&mut scope, &ast)?; println!("5 * 10 = {result}"); // prints 50 Ok(()) } ``` -------------------------------- ### Communicate in Duplex with Rhai Engine in Separate Thread Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Shows how to establish duplex communication with a Rhai `Engine` running in a separate thread using a pair of MPSC channels. This allows for bidirectional message passing. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; use std::thread; use std::sync::mpsc::{channel, Sender, Receiver}; fn main() { let (tx_to_thread, rx_from_main) = channel::(); let (tx_to_main, rx_from_thread) = channel::(); let handle = thread::spawn(move || { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register a function to send messages back to the main thread engine.register_fn("send_to_main", move |msg: String| { if tx_to_main.send(msg).is_err() { println!("[Thread] Failed to send message to main thread."); } }); // Register a function to receive messages from the main thread engine.register_fn("receive_from_main", move || -> String { match rx_from_main.recv() { Ok(msg) => msg, Err(_) => "Error receiving message".to_string(), } }); let script = "loop { let msg = receive_from_main(); if msg == "STOP" { break; } send_to_main(format!("Thread received: {}", msg)); } send_to_main("Thread shutting down.".to_string()); "; if let Err(e) = engine.run(&script) { println!("[Thread] Script error: {}", e); } }); // Send messages from main thread to the script tx_to_thread.send("Hello from main!".to_string()).unwrap(); tx_to_thread.send("How are you?".to_string()).unwrap(); tx_to_thread.send("STOP".to_string()).unwrap(); // Signal to stop // Receive messages from the script for received in rx_from_thread { println!("Main received: {}", received); } handle.join().unwrap(); println!("Main thread finished."); } ``` -------------------------------- ### Generate Reserved Symbol Table Source: https://github.com/rhaiscript/rhai/blob/main/tools/reserved.txt Use GNU gperf to generate the reserved symbol table from the configuration file. ```bash gperf reserved.txt ``` -------------------------------- ### Pause, Resume, and Stop Rhai Engine in Separate Thread Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Demonstrates how to pause, resume, and stop a Rhai `Engine` running in a separate thread using an MPSC channel for communication. This is useful for managing long-running scripts. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; use std::thread; use std::sync::mpsc::{channel, Sender, Receiver}; enum ControlMsg { Pause, Resume, Stop, } fn main() { let (tx, rx) = channel::(); let (result_tx, result_rx) = channel::(); let handle = thread::spawn(move || { let mut engine = Engine::new(); let mut scope = Scope::new(); scope.push("counter", 0); let script = "loop { counter += 1; // Check for control messages periodically if let Ok(msg) = rx.try_recv() { match msg { ControlMsg::Pause => { println!("[Thread] Paused. Waiting for Resume..."); loop { if let Ok(ControlMsg::Resume) = rx.recv() { println!("[Thread] Resumed."); break; } if let Ok(ControlMsg::Stop) = rx.recv() { println!("[Thread] Stopped during pause."); return; } } } ControlMsg::Stop => { println!("[Thread] Stopped."); return; } ControlMsg::Resume => { /* Should not happen here */ } } } std::thread::sleep(std::time::Duration::from_millis(10)); }"; let ast = engine.compile(script).unwrap(); engine.run_ast_with_scope(&mut scope, &ast).unwrap_or_else(|err| { println!("[Thread] Script error: {}", err); // Attempt to send final counter value even on error if let Some(val) = scope.get_or_default::("counter") { let _ = result_tx.send(val); } }); }); // Give the thread some time to start thread::sleep(std::time::Duration::from_millis(100)); // Pause the script tx.send(ControlMsg::Pause).unwrap(); thread::sleep(std::time::Duration::from_secs(1)); // Allow script to pause // Resume the script tx.send(ControlMsg::Resume).unwrap(); thread::sleep(std::time::Duration::from_millis(500)); // Let it run for a bit // Stop the script tx.send(ControlMsg::Stop).unwrap(); handle.join().unwrap(); // Try to get the final counter value if the script sent it before stopping if let Ok(final_count) = result_rx.try_recv() { println!("Final counter value received: {}", final_count); } else { println!("Script finished without sending a final value."); } } ``` -------------------------------- ### Serde Integration for Rhai Dynamic Values Source: https://context7.com/rhaiscript/rhai/llms.txt Serialize and deserialize between Rust types and Rhai's Dynamic values using serde. Requires the `serde` feature to be enabled. ```rust use rhai::{Engine, Dynamic, Map, EvalAltResult}; use rhai::serde::{from_dynamic, to_dynamic}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] struct Config { name: String, value: i64, enabled: bool, tags: Vec, } fn main() -> Result<(), Box> { // Serialize Rust struct to Dynamic let config = Config { name: "MyConfig".into(), value: 42, enabled: true, tags: vec!["a".into(), "b".into()], }; let dynamic: Dynamic = to_dynamic(config.clone()).unwrap(); println!("Serialized: {dynamic:?}"); // Deserialize Dynamic back to Rust struct let restored: Config = from_dynamic(&dynamic).unwrap(); assert_eq!(config, restored); // Create Dynamic object map in script and deserialize let engine = Engine::new(); let result: Dynamic = engine.eval(r#" #{ name: "FromScript", value: 100, enabled: false, tags: ["x", "y", "z"] } "#)?; let from_script: Config = from_dynamic(&result).unwrap(); println!("From script: {:?}", from_script); assert_eq!(from_script.name, "FromScript"); assert_eq!(from_script.value, 100); Ok(()) } ``` -------------------------------- ### Evaluate Multiple Rhai Scripts with Common Scope Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Shows how to evaluate two pieces of code in separate runs while using a common `Scope`. This allows sharing variables and state between different script executions. ```rust use rhai::{Engine, Scope, AST, CallFnOptions}; fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // First script: define a variable engine.eval_with_scope::<()>({ let mut s = scope.clone(); s.push("x", 10); "let y = x * 2;" }).unwrap(); // Second script: use the variable defined in the first script let result = engine.eval_with_scope::({ let mut s = scope.clone(); s.push("x", 5); // Overwrite x for this scope "let z = x + y;" }).unwrap(); println!("Result: {}", result); } ``` -------------------------------- ### Rhai Reserved Symbols Configuration Source: https://github.com/rhaiscript/rhai/blob/main/tools/reserved.txt The configuration file defining reserved symbols, keywords, and their associated feature flags or call styles. ```text %global-table %struct-type %omit-struct-type %define initializer-suffix ,false,false,false struct reserved; %% # reserved under certain flags # ?., cfg!(feature = no_object), false, false ?[, cfg!(feature = no_index), false, false fn, cfg!(feature = no_function), false, false private, cfg!(feature = no_function), false, false import, cfg!(feature = no_module), false, false export, cfg!(feature = no_module), false, false as, cfg!(feature = no_module), false, false # # reserved symbols # ===, true, false, false !==, true, false, false ->, true, false, false <-, true, false, false ?, true, false, false :=, true, false, false :;, true, false, false ~, true, false, false !., true, false, false ::<, true, false, false (*, true, false, false *), true, false, false "#", true, false, false "#!", true, false, false @, true, false, false $, true, false, false ++, true, false, false --, true, false, false ..., true, false, false <|, true, false, false |>, true, false, false # # reserved keywords # public, true, false, false protected, true, false, false super, true, false, false new, true, false, false use, true, false, false module, true, false, false package, true, false, false var, true, false, false static, true, false, false shared, true, false, false with, true, false, false is, true, false, false goto, true, false, false exit, false, false, false match, true, false, false case, true, false, false default, true, false, false void, true, false, false null, true, false, false nil, true, false, false spawn, true, false, false thread, true, false, false go, true, false, false sync, true, false, false async, true, false, false await, true, false, false yield, true, false, false # # keyword functions # print, true, true, false debug, true, true, false type_of, true, true, true eval, true, true, false Fn, true, true, false call, true, true, true curry, true, true, true this, true, false, false is_def_var, true, true, false is_def_fn, cfg!(not(feature = no_function)), true, false is_shared, cfg!(not(feature = no_closure)), true, true ``` -------------------------------- ### Register Indexer Operations in Rust Source: https://context7.com/rhaiscript/rhai/llms.txt Enable bracket notation access for custom types by registering indexer functions. ```rust use rhai::{Engine, EvalAltResult}; #[derive(Debug, Clone)] struct MyArray { data: Vec, } impl MyArray { fn new() -> Self { Self { data: vec![10, 20, 30, 40, 50] } } fn get(&mut self, index: i64) -> i64 { self.data[index as usize] } fn set(&mut self, index: i64, value: i64) { self.data[index as usize] = value; } fn len(&mut self) -> i64 { self.data.len() as i64 } } fn main() -> Result<(), Box> { let mut engine = Engine::new(); engine.register_type::(); engine.register_fn("new_array", MyArray::new); engine.register_fn("len", MyArray::len); // Register indexer get and set engine.register_indexer_get(MyArray::get); engine.register_indexer_set(MyArray::set); // Or use register_indexer_get_set for both at once // engine.register_indexer_get_set(MyArray::get, MyArray::set); // Use bracket notation in scripts let result = engine.eval::(r#" let arr = new_array(); let sum = 0; for i in 0..arr.len() { sum += arr[i]; } arr[2] = 100; // modify element sum + arr[2] // returns 150 + 100 = 250 "#)?; println!("Result: {result}"); // prints 250 Ok(()) } ``` -------------------------------- ### Handle Function Pointers and Closures Source: https://context7.com/rhaiscript/rhai/llms.txt Rhai supports closures that capture variables and higher-order functions like map, filter, and reduce. ```rust use rhai::{Engine, EvalAltResult, FnPtr, AST}; fn main() -> Result<(), Box> { let engine = Engine::new(); // Script that returns a closure let ast: AST = engine.compile(r#" let multiplier = 10; // Return a closure that captures 'multiplier' |x| { multiplier += 1; // captured variable is mutable x * multiplier } "#)?; // Evaluate to get the function pointer let closure: FnPtr = engine.eval_ast(&ast)?; // Create a Rust closure that calls the Rhai closure let func = move |x: i64| -> i64 { closure.call(&engine, &ast, (x,)).unwrap() }; // Each call sees the modified captured variable let r1 = func(5); // 5 * 11 = 55 let r2 = func(5); // 5 * 12 = 60 let r3 = func(5); // 5 * 13 = 65 println!("Results: {r1}, {r2}, {r3}"); // prints 55, 60, 65 // Higher-order functions in scripts let result = engine.eval::(r#" let numbers = [1, 2, 3, 4, 5]; // Use built-in map function with closure let doubled = numbers.map(|x| x * 2); // Use filter let evens = doubled.filter(|x| x % 4 == 0); // Use reduce evens.reduce(|sum, x| sum + x, 0) "#)?; println!("Sum of evens: {result}"); // prints 12 (4 + 8) Ok(()) } ``` -------------------------------- ### Register Rust Type with CustomType Trait Source: https://github.com/rhaiscript/rhai/blob/main/examples/README.md Demonstrates registering a Rust type using the `CustomType` trait, enabling methods, getters, and setters within Rhai scripts. This is an alternative to direct method registration. ```rust use rhai::{Engine, Scope, AST, CallFnOptions, CustomType}; #[derive(Debug, Clone, Default)] struct MyStruct { data: i32, } impl MyStruct { fn new() -> Self { MyStruct { data: 0 } } fn get_data(&self) -> i32 { self.data } fn set_data(&mut self, value: i32) { self.data = value; } fn add(&mut self, value: i32) { self.data += value; } } // Implement CustomType for the Rust struct impl CustomType for MyStruct { fn build(build: &mut rhai::TypeBuilder) { build .with_fn("new", MyStruct::new) .with_get("data", MyStruct::get_data) .with_set("data", MyStruct::set_data) .with_fn("add", MyStruct::add); } } fn main() { let mut engine = Engine::new(); let mut scope = Scope::new(); // Register the type using CustomType implementation engine.register_type::(); // Use the registered type in a script let ast = engine.compile("let x = new(); x.data = 10; x.add(5); x.data").unwrap(); let result = engine.eval_ast_with_scope::(&mut scope, &ast).unwrap(); println!("Script result: {}", result); } ``` -------------------------------- ### Register Custom Types Source: https://context7.com/rhaiscript/rhai/llms.txt Exposes Rust structs to Rhai by implementing CustomType and using TypeBuilder. Allows script-side instantiation and method invocation. ```rust use rhai::{Engine, EvalAltResult, CustomType, TypeBuilder}; #[derive(Debug, Clone, PartialEq, CustomType)] #[rhai_type(extra = Self::build_extra)] struct Point { x: f64, y: f64, } impl Point { fn new(x: f64, y: f64) -> Self { Self { x, y } } fn distance(&mut self) -> f64 { (self.x * self.x + self.y * self.y).sqrt() } fn translate(&mut self, dx: f64, dy: f64) { self.x += dx; self.y += dy; } fn build_extra(builder: &mut TypeBuilder) { builder .with_name("Point") .with_fn("new_point", Self::new) .with_fn("distance", Self::distance) .with_fn("translate", Self::translate); } } fn main() -> Result<(), Box> { let mut engine = Engine::new(); // Build the custom type with all its methods engine.build_type::(); // Use the custom type in scripts let result = engine.eval::(r#" let p = new_point(3.0, 4.0); p.translate(1.0, 1.0); p.distance() "#)?; println!("Distance: {result}"); // prints 5.656... (sqrt(16+25)) // Access properties directly let x = engine.eval::("let p = new_point(10.0, 20.0); p.x")?; assert_eq!(x, 10.0); // Modify properties let result = engine.eval::(r#" let p = new_point(0.0, 0.0); p.x = 5.0; p.y = 12.0; p "#)?; assert_eq!(result, Point::new(5.0, 12.0)); Ok(()) } ``` -------------------------------- ### Register Property Getters and Setters in Rust Source: https://context7.com/rhaiscript/rhai/llms.txt Use register_get, register_set, or register_get_set to expose struct fields or computed properties to Rhai scripts. ```rust use rhai::{Engine, EvalAltResult}; #[derive(Debug, Clone)] struct Rectangle { width: f64, height: f64, } impl Rectangle { fn new() -> Self { Self { width: 0.0, height: 0.0 } } fn get_width(&mut self) -> f64 { self.width } fn set_width(&mut self, w: f64) { self.width = w; } fn get_height(&mut self) -> f64 { self.height } fn set_height(&mut self, h: f64) { self.height = h; } fn get_area(&mut self) -> f64 { self.width * self.height } } fn main() -> Result<(), Box> { let mut engine = Engine::new(); // Register the custom type engine.register_type::(); engine.register_fn("new_rect", Rectangle::new); // Register individual getters and setters engine.register_get("width", Rectangle::get_width); engine.register_set("width", Rectangle::set_width); // Or use register_get_set for both at once engine.register_get_set("height", Rectangle::get_height, Rectangle::set_height); // Read-only property (getter only) engine.register_get("area", Rectangle::get_area); // Use properties in scripts let area = engine.eval::(r#" let r = new_rect(); r.width = 10.0; r.height = 5.0; r.area // computed property "#)?; println!("Area: {area}"); // prints 50.0 Ok(()) } ``` -------------------------------- ### Configuring Safety Limits in Rhai Engine Source: https://context7.com/rhaiscript/rhai/llms.txt Set engine limits to prevent resource exhaustion and infinite loops, such as maximum call stack depth, operations, string size, and array size. ```rust use rhai::{Engine, EvalAltResult}; fn main() -> Result<(), Box> { let mut engine = Engine::new(); // Set maximum call stack depth (prevents stack overflow from recursion) engine.set_max_call_levels(32); // Set maximum operations allowed (prevents infinite loops) engine.set_max_operations(10_000); // Set maximum string length engine.set_max_string_size(10_000); // Set maximum array size engine.set_max_array_size(1_000); // Set maximum object map size engine.set_max_map_size(100); // Set maximum expression depth engine.set_max_expr_depths(64, 32); // Set maximum number of variables engine.set_max_variables(100); // Set maximum number of modules engine.set_max_modules(10); // This will fail due to operation limit let result = engine.eval::(r#" let sum = 0; for i in 0..1_000_000 { sum += i; } sum "#); match result { Err(err) => println!("Script terminated: {err}"), Ok(v) => println!("Result: {v}"), } // Progress callback to monitor and terminate scripts engine.on_progress(|count| { if count % 1000 == 0 { println!("Operations: {count}"); } // Return Some(value) to terminate, None to continue if count > 5000 { Some("Terminated early!".into()) } else { None } }); Ok(()) } ``` -------------------------------- ### Rhai Keywords and Symbols Mapping Source: https://github.com/rhaiscript/rhai/blob/main/tools/keywords.txt This section defines the mapping between Rhai keywords/symbols and their corresponding Rust Token variants. It is used in the tokenizer implementation. ```plaintext %global-table %struct-type %omit-struct-type %define initializer-suffix ,Token::EOF struct keyword; %% {, Token::LeftBrace }, Token::RightBrace (, Token::LeftParen ), Token::RightParen [, Token::LeftBracket ], Token::RightBracket (), Token::Unit +, Token::Plus -, Token::Minus *, Token::Multiply /, Token::Divide ;, Token::SemiColon :, Token::Colon ::, Token::DoubleColon =>, Token::DoubleArrow _, Token::Underscore ",", Token::Comma ., Token::Period ?., Token::Elvis ??, Token::DoubleQuestion ?[", Token::QuestionBracket .., Token::ExclusiveRange ..=, Token::InclusiveRange "#{", Token::MapStart =, Token::Equals true, Token::True false, Token::False let, Token::Let const, Token::Const if, Token::If else, Token::Else switch, Token::Switch do, Token::Do while, Token::While until, Token::Until loop, Token::Loop for, Token::For in, Token::In !in, Token::NotIn <, Token::LessThan >, Token::GreaterThan <=, Token::LessThanEqualsTo >=, Token::GreaterThanEqualsTo ==, Token::EqualsTo !=, Token::NotEqualsTo !, Token::Bang |, Token::Pipe ||, Token::Or &, Token::Ampersand &&, Token::And continue, Token::Continue break, Token::Break return, Token::Return throw, Token::Throw try, Token::Try catch, Token::Catch +=, Token::PlusAssign -=, Token::MinusAssign *=, Token::MultiplyAssign /=, Token::DivideAssign <<=, Token::LeftShiftAssign >>=, Token::RightShiftAssign &=, Token::AndAssign |=, Token::OrAssign ^=, Token::XOrAssign <<, Token::LeftShift >>, Token::RightShift ^, Token::XOr "%", Token::Modulo "%=", Token::ModuloAssign **, Token::PowerOf **=, Token::PowerOfAssign fn, Token::Fn private, Token::Private import, Token::Import export, Token::Export as, Token::As ``` -------------------------------- ### Call Script-Defined Functions Source: https://context7.com/rhaiscript/rhai/llms.txt Invokes functions defined within a Rhai script from Rust. Supports passing arguments as tuples and binding 'this' pointers using CallFnOptions. ```rust use rhai::{Engine, Scope, EvalAltResult, CallFnOptions, Dynamic}; fn main() -> Result<(), Box> { let engine = Engine::new(); // Compile a script with function definitions let ast = engine.compile(r#" fn add(x, y) { x + y } fn greet(name) { `Hello, ${name}!` } fn process(data) { data.len() + offset } fn action(x) { this += x; } "#)?; let mut scope = Scope::new(); scope.push("offset", 10_i64); // Call script functions with arguments (arguments as tuple) let result = engine.call_fn::(&mut scope, &ast, "add", (40_i64, 2_i64))?; assert_eq!(result, 42); // Single argument requires tuple syntax with trailing comma let greeting = engine.call_fn::(&mut scope, &ast, "greet", ("World",))?; println!("{greeting}"); // prints "Hello, World!" // No arguments - use empty tuple let ast2 = engine.compile("fn get_value() { 42 }")?; let value = engine.call_fn::(&mut scope, &ast2, "get_value", ())?; assert_eq!(value, 42); // Call with this pointer binding using CallFnOptions let mut this_value: Dynamic = 1_i64.into(); let options = CallFnOptions::new().bind_this_ptr(&mut this_value); engine.call_fn_with_options::<()>(options, &mut scope, &ast, "action", (41_i64,))?; assert_eq!(this_value.as_int().unwrap(), 42); Ok(()) } ```