The purpose of Rhai is not to be blazing fast, but to make it as easy and versatile as possible to integrate with native Rust applications.
What you lose from running an AST walker, you gain back from increased flexibility.
|Fibonacci||2.25s||3.2s||0.6s||0.07s||stresses recursive function calls|
|1M loop||0.13s||0.2s||0.08s||0.05s||a simple counting loop (1 million iterations) that must run as fast as possible|
|Prime numbers||0.85s||1.2s||0.4s||0.09s||a closer-to-real-life calculation workload|
Essential AST and runtime data structures are packed small and kept together to maximize cache friendliness.
Functions are dispatched based on pre-calculated hashes.
Variables are mostly accessed through pre-calculated offsets to the variables file (a
It is seldom necessary to look something up by name.
Function resolutions are cached so they do not incur lookup costs after a couple of calls.
Maintaining a scope chain is deliberately avoided by design so function scopes do not pay any speed penalty. This allows variables data to be kept together in a contiguous block, avoiding allocations and fragmentation while being cache-friendly.
Rhai uses immutable strings to bypass cloning issues.
Rhai deliberately keeps the language small and lean by omitting advanced language features such as classes, inheritance, interfaces, generics, first-class functions/closures, pattern matching, monads (whatever), concurrency, async etc.
Focus is on flexibility and ease of use instead of a powerful, expressive language.
In actual practice, it is usually best to expose a Rust API into Rhai for scripts to call.
All the core functionalities should be written in Rust, with Rhai being the dynamic control layer.
This is similar to some dynamic languages where most of the core functionalities reside in a C/C++ standard library (e.g. Python 3).