Skip to content

Version 1.27.0

Latest

Choose a tag to compare

@schungx schungx released this 21 Oct 13:09
aaf9090

This is a big release that officially remove the experimental qualifier to the Rhai Grain transpiler and VM.

A new feature, no_ast is added to completely remove the AST, parser, tokenizer and optimizer from the build. Such a build can only run Rhai Grain bytecodes; it cannot parse Rhai script text.

Lazy-loading is now also supported natively in all modes. Lazy-loaded modules only look up functions when they're being called, thus substantially cutting down runtime footprint.

There are a large number of bug fixes as well, in addition to some performance improvements.

Bug fixes

  • (Fuzzing) Fixed missing data-race condition in native function callbacks (#1161).
  • Errors raised during built-in binary operations (such as 100 / 0) now contain proper Position information (identified by Rhai Grain ).
  • Number of operations reported to Engine::on_progress now properly count scripted function calls from native Rust functions (identified by Rhai Grain). Previously all operation counts performed by a scripted function callback (including a closure) from a native Rust function are discarded.
  • Capturing the caller's scope via call!(fnptr, args...) now works properly in Rhai Grain.
  • .shared() method calls now work property in Rhai Grain.
  • Missing capabilities in scripts related to some constants (especially in switch cases, arrays and object maps) now work properly in Rhai Grain (thanks @yinho999 #1182).
  • Failed function resolutions are now cached properly for calls with other than two arguments - a regression from version 1.10 (thanks @ImTheSquid #1183).
  • A function resolution cache is now created (if none exists) when entering a block. Previously, a loop body evaluated with no cache yet would resolve every function call again on each iteration (thanks @ImTheSquid #1183).
  • Assigning a constant into a container variable (array, map etc.) now works properly without making the value read-only (thanks @cuervolu #1187).
  • Under sync, a method call on a shared variable with a closure argument that captures the same variable now raises ErrorDataRace, as it does without sync, instead of panicking while resolving the function. Dynamic::is_locked now returns true under sync when the value cannot be read, which includes being locked for write by the current thread (thanks @yuvalrakavy #1190).
  • A panic inside a native Rust function that takes its first argument by value, called with a variable first argument, now unwinds normally and can be caught by catch_unwind. Previously the copied first argument was restored only on the normal-return path, so the unwind tripped an internal assertion and the process aborted (thanks @yuvalrakavy #1189).

Rhai Grain is now at feature parity with the AST interpreter

  • Rhai Grain is no longer experimental.
  • New no_ast feature added that excludes the AST, its interpreter, the tokenizer, parser and optimizer for a minimized build (#1160). Under this feature flag, the Rhai Grain VM is the only venue to evaluate a script.
  • Bare function names are now supported as variables in Rhai Grain (#1158).
  • Custom syntax that does not modify the scope is now fully supported in Rhai Grain (#1173). Custom syntax that modifies the scope remains non-lowerable fragments.
  • Importing modules, the global:: namespace, the export and import statements are all supported by Rhai Grain (#1179).
  • The Engine::on_map_missing_property callback now works with Rhai Grain (#1164).
  • The Engine::on_def_var callback now works with Rhai Grain (#1170). However, the nesting level reported by VarDefInfo is always zero, since all nesting information is lost once the AST is lowered to bytecodes.
  • .call(fnptr, args...) and .curry(...) method calls now work in any position (instead of only on a chain's root) in Rhai Grain.
  • Rhai Grain bytecodes transpilation is optimized (thanks @ImTheSquid #1156).
  • Rhai Grain VM is now at par or faster than the AST interpreter for scripts index assignments (#1163).
  • Rhai Grain VM is now at par or faster than the AST interpreter for scripts with callbacks (#1159).
  • Rhai Grain VM is now 1.8-2x faster than the AST interpreter when running switch statements (#1162, #1165).
  • Rhai Grain VM is now 2x faster than the AST interpreter when running !<bool> expressions.
  • rhai-run now supports loading and executing Rhai Grain bytecode files if the grain feature is enabled (#1160).
  • The example grain_dump is now split into two CLI tools in bin: grain-compile, which compiles a Rhai script into Rhai Grain bytecodes, and grain-dump which dissembles a Rhai Grain bytecodes files (#1160).
  • grain-dump now disassembles Rhai Grain bytecodes files with more complete information (#1168).

Lazy loading of plugin module functions

  • Contributed by @ImTheSquid #1178.
  • Functions in plugin modules can now be resolved lazily: a Module can hold static manifests of plugin modules (generated via #[export_module(manifest)] and exported_manifest!) alongside its normal functions, and functions in manifests are not registered but looked up only when called.
  • Lazy-loading is opt-in.
  • Every package defined via def_package! now has a new_lazy constructor that keeps the functions of plugin modules within their manifests, looking them up only when called. For example, Engine::new_raw() with StandardPackage::new_lazy() uses about 16x less heap than Engine::new(), and builds the standard library about 8x faster, at the cost of slower function resolution the first time a function is called.
  • Lazy-load plugin's are supported by both the AST interpreter and Rhai Grain.
  • Use the new Module::from_manifest (the lazy exported_module!) or Module::combine_manifest (the lazy combine_with_exported_module!) API to create a lazy-loaded module or add the manifest of lazy-loaded functions to a module.
  • Module::register_lazy_functions registers all lazy-loaded functions up-front instead, for the same speed as before.

Feature flag to replace smartstring with compact_str

  • Contributed by @hoppworks #1175.
  • A new compact_str feature that uses compact_str instead of smartstring for identifiers and object map keys
  • smartstring is archived and unmaintained (RUSTSEC-2026-0249) and licensed under MPL-2.0+ (#897), whilecompact_str is licensed under the more permissive MIT.
  • Under compact_str, the key type of Map (and Identifier under internals) is CompactString instead of SmartString.
  • Under compact_str, serde-serializable types use CompactString directly instead of String since the serde feature of compact_str is no-std-safe, unlike that of smartstring.
  • compact_str is intended to become the default in version 2.0.

Enhancements

  • Added AGENTS.md files and a language reference cheat-sheet for AI (RHAI_CHEATSHEET.md).
  • String interpolation now resolves to_string with the evaluation's function resolution caches, instead of creating a new NativeCallContext (which cloned the global runtime state) for each interpolated value (thanks @ImTheSquid #1183).