I've embedded Rhai in Glicol to support sample-level audio synthesis, and the performance is [acceptable]. Real-time audio is very computationally intensive, after all.
I've tried various embedded scripting languages in Rust, but Rhai probably has the best documentation and playground.
Small comment, but why does the text editor show an "overwrite" indictor instead of a cursor indicator when I try to change something in the playground? https://i.imgur.com/OAXbXQ2.png
I tried typing different things, and despite showing an overwrite indicator it seems to be in a normal insert mode. I have no idea which side of the highlight my characters will be inserted on though.
1. This is much easier to embed in Rust projects than Lua.
2. Doesn't make the mistake of 1-based indexing.
3. Generally much more modern and nicer language than Lua.
The reasons I've found not to use it:
1. Very slow - even slower than Python! Of course often this won't matter but it would be nice if it was fast.
2. No support for type annotations.
I've used it before for a config file, and it worked well there. I think if I wanted anything much more complex though I would just embed V8. Then you get a language that everyone already knows and very good type annotations.
I don't think "ecosystem" really matters much for embeddable languages like this. You app is the ecosystem.
Is there something in specific that makes this easier to use in Rust projects compared to the Lua wrappers/bindings like mlua[0]? Or is it just an overall ergonomics thing?
Genuine question, as I don't have any prior experience embedding any scripting language into a Rust project.
A lot of people would call this a feature, not a mistake. 0-based indexing came from programming languages typically calculating addresses in the memory layout of an array. Whereas in mathematics (the background of the Lua inventor) sequence positions typically start at 1. Also, humans generally start counting from 1. This is intuitive in languages like SQL which also use 1-based indexing.
It could be argued that 0-based indexing was the mistake since it actually conflates two concepts, the memory layout in the machine, and the actual sequence you want to index.
> 2. Doesn't make the mistake of 1-based indexing.
Why is this a mistake? I've used 0-based languages primarily, but ergonomically, 1-based languages like Awk and Smalltalk are fine too, making some code slightly harder to write and other code slightly easier. Overall, I've found it to be a wash. If anything, in a pointer-less language, pedagogically, 1-based is more intuitive for novices.
P.S. Classic VB and VBA had an odd feature where one could choose the base for themselves, using the Option Base feature.
I haven't tried Rhai and I wouldn't call myself anything more than a casual Lua user, but from a glance, these could be some reasons I see others using Rhai instead of Lua:
- Closer to Rust syntax/data structures, so easier if you already know Rust but don't know Lua
- Built-in serde (popular Rust de/serializer) support, if you need that
- Not sure if existing Rust<>Lua crates have it, but the debugging interface looks like the beginning of something useful (https://rhai.rs/book/engine/debugging/)
- Made in Rust, so again, if you already use Rust (which you do, if this crate is an option) it'll most likely to be easier to change for your needs, than modifying a Lua runtime
Personally, I'd probably still go for Lua rather than something like Rhai, if I had to choose something for algol-like scripting support. Otherwise I think Steel (https://github.com/mattwparas/steel) looks like the most useful scripting environment for Rust currently.
Thanks for these examples. I wrote a POC tool to apply common patches to multiple projects. These patches where supposed to be written as scripts. Idea was to run the same set of action on projects with different layouts based on their names etc. So a normal patch would not work.
Anyways I used mlua because it was the easiest way to get some scripting to run. But I faced issues writing the scripts. Basic operations had to come from the host like “string endsWith”, “list contains” and some other basic methods (Can’t remember which ones it where in detail). That mixed with the fact that Lua is so different. I knew I could not give this to any other dev in my team without a lot of instructions how lua handles stuff differently. So it’s nice to know I have a potential new goto solution when facing this again. Especially the ability to dumb it down.
> I’ve tried integrating with mlua. It works, but Rhai is simpler to embed. Simpler to build.
I'm curious what challenges you faced when embedding Lua via mlua? I've done it many times in projects and I've always found it to be trivial.
$ cargo add mlua --features lua54,vendored
And then bringing it into Rust is simple like:
let lua = mlua::Lua();
Are your requirements more complicated than this that makes it not as easy? I've never had to mess around with linking the system Lua, or anything else. mlua just brings its own Lua distribution with the `vendored` feature.
I've worked with moai (1) reasonably extensively, and the lua in it is not easy to use and sucks.
Specifically, a project that is composed entirely of lua with no other dependencies is indeed very easy to build and maintain. I agree.
However, my $0.02 would be that if you plan to have a large project with many 3rd party dependencies, then cmake, visual studio, C++, lua and the time spent jumping between them and maintaining those dependencies in a custom build toolchain will cost you more time and effort than the benefits that lua offers (2).
...and you do, indeed, need to do that, because c++ lacks a centralized package ecosystem and unified build tooling; and as operating systems change, existing builds stop working.
So, yes, you may consider my answer to be 'rust'; but the actual answer is 'not C++ and not cmake'.
That all said, lua is a more mature better system than this is currently, with good resources online and an active community. In cases where a small dependency tree can be maintained, it's still the best choice I'm aware of.
I'm simply pointing out that there are reasons you would pick it over lua, and I think they're quite compelling for cases where the future dependency graph of your project is unknown / unknown.
"Not C++ and not cmake" is right on the money. Also Rhai seems to be in its infancy. Despite its current limitations, there's an argument for supporting it early to see where this road will take us. Hopefully to a relatively fast and more feature rich scripting language and engine that fit snuggly into Rust projects, which would be fantastic.
Also played around with moai back in the days when Doublefine had their Kickstarter and choose it as the base framework. But I found it hard to make the jump between c++ and lua etc. The docs made the decision when to write what simple. Everything lua until it becomes a bottleneck. But when to start from nothing this decision is still hard. Pull a lib and expose it to Lua or try to write the lib in Lua? Stuff like that. I needed a basic triangulation algorithm for 2d shapes and implemented that myself from a paper about the ear clipping algorithm. Fun times. All hobby stuff just for fun and games.
For one, I don't like the Lua language. This is just my opinion but I dislike the syntax and semantics. It's better than vimscript for configuring neovim but that's a pretty low bar.
And if the argument is "pick a long-supported standard as an embedded language" then I'd rather go with Lisp (e.g. https://github.com/mattwparas/steel). Historically, Lua is the fun hobby project newcomer.
There are a bunch of crates available for scripting in Rust/Bevy (Rhai, Rune, Luau, Teal, etc), anyone who've tried specifically Rhai with specifically Bevy before and could share their experience?
I was also curious. Looking at the code, it seems values are Boxed, but there's a special type called Shared that is an Rc-RefCell (unless Send-enabled.):
// Also handle case where target is a `Dynamic` shared value
// (returned by a variable resolver, for example)
Couldn't find any other information about a GC, so guessing this is pure ref-counting. Speaking of potential memory leaks, there's also string interning happening. I agree this seems to be for short-lived contexts right now.
When I last read about Rhai it was apparently very slow such that it was simply faster and more ergonomic writing scripts in Rust itself, has that changed?
According to the documentation it evaluates by walking the ast, so yes, this is considered very slow. The readme also mentions 1 million loop iterations in 0.14s (140ms). Even my unoptimised naive lips like language [1] (also implemented via a ast walker) does the same in 28.67ms - so yes id consider this pretty slow.
As someone who is also implementing a naive "walk the AST" evaluator for lisp, what would be considered OK/fast/not-slow in the case for 1 million loop iterations? Would ~30ms be considered fast or "not-slow"?
Not sure when you read this, but I can tell you that two years ago it was VERY slow. I used it for a game and I had to redo it in lua some months later because it was a big bottleneck. I don't have more up to date information.
I use it in rsmodules https://github.com/fretn/rsmodules and I’m very happy with my choice. Was easy to implement and the earliest versions already offered what I needed.
If the rhai author is reading this: thank you for this nice piece of software
For anyone curious for more details, miri works in a different way than valgrind. it is an interpreter for rust that does additional checks at runtime to detect undefined behavior. This allows it to be fully deterministic, simulate other platforms, and do additional checks that I don’t think would be possible for valgrind
Someone else already answered, just a tip for future searches: If you know somewhat the context (in this case Rust), adding just one keyword to your query (in this case "Miri Rust") will give you the right answer as the first hit :)
Cool! But wouldn't the way to go for scripting in a language these days to just compile to WASM and run in an embedded micro-VM? Why hasn't Rhai made this choice?
1. That require compiling to wasm, and for some use cases, you don't want a compilation step. That might even be a big part of why you are using a scripting language.
2. That requires an entire wasm runtime, which is a pretty heavy dependency
Here are two real-world examples of using Rhai:
https://glicol.org/tour#meta1
https://glicol.org/tour#meta2
I've embedded Rhai in Glicol to support sample-level audio synthesis, and the performance is [acceptable]. Real-time audio is very computationally intensive, after all.
I've tried various embedded scripting languages in Rust, but Rhai probably has the best documentation and playground.
For reference, see this survey:
https://www.boringcactus.com/2020/09/16/survey-of-rust-embed...
Recent Koto Lang also looks promising:
https://koto.dev/
Other embedded scripting language is Steel [1]. It’s being considered to be used in Helix [2].
[1] https://github.com/mattwparas/steel
[2] https://github.com/helix-editor/helix/pull/8675
Another example using Rhai that was posted here a week or two ago: this (in-browser) 3D modeling software using Fidget.
https://www.mattkeeter.com/projects/fidget/demo/
> https://www.boringcactus.com/2020/09/16/survey-of-rust-embed...
For some reason, clicking on that link automatically downloads a file named "Human Fart.wav"...
Small comment, but why does the text editor show an "overwrite" indictor instead of a cursor indicator when I try to change something in the playground? https://i.imgur.com/OAXbXQ2.png
I tried typing different things, and despite showing an overwrite indicator it seems to be in a normal insert mode. I have no idea which side of the highlight my characters will be inserted on though.
This is totally my personal preference :)
I thought it was clearer this way.
But there is a new version of the website under construction:
https://glicol-x.netlify.app/
It will be changed to the normal look.
Not going to dissuade anyone from having fun hobby projects.
But legitimate question: why would I choose this over Lua, which is probably faster, super easy to embed and has a larger ecosystem.
(Please, saying "Rust" or memory-safety can be assumed already to be understood, but not considered compelling arguments)
1. This is much easier to embed in Rust projects than Lua.
2. Doesn't make the mistake of 1-based indexing.
3. Generally much more modern and nicer language than Lua.
The reasons I've found not to use it:
1. Very slow - even slower than Python! Of course often this won't matter but it would be nice if it was fast.
2. No support for type annotations.
I've used it before for a config file, and it worked well there. I think if I wanted anything much more complex though I would just embed V8. Then you get a language that everyone already knows and very good type annotations.
I don't think "ecosystem" really matters much for embeddable languages like this. You app is the ecosystem.
Is there something in specific that makes this easier to use in Rust projects compared to the Lua wrappers/bindings like mlua[0]? Or is it just an overall ergonomics thing?
Genuine question, as I don't have any prior experience embedding any scripting language into a Rust project.
[0]: https://github.com/mlua-rs/mlua
10 replies →
"suffer" 1-indexing vs no LSP, no books, no test-of-time, no ecosystem, no type annotations, slower.
I don't mean to be overly harsh, but this is just not a valid/serious answer. The other reasons are fine. This is pure bike-shedding.
11 replies →
> Doesn't make the mistake of 1-based indexing.
A lot of people would call this a feature, not a mistake. 0-based indexing came from programming languages typically calculating addresses in the memory layout of an array. Whereas in mathematics (the background of the Lua inventor) sequence positions typically start at 1. Also, humans generally start counting from 1. This is intuitive in languages like SQL which also use 1-based indexing.
It could be argued that 0-based indexing was the mistake since it actually conflates two concepts, the memory layout in the machine, and the actual sequence you want to index.
9 replies →
> 2. Doesn't make the mistake of 1-based indexing.
Why is this a mistake? I've used 0-based languages primarily, but ergonomically, 1-based languages like Awk and Smalltalk are fine too, making some code slightly harder to write and other code slightly easier. Overall, I've found it to be a wash. If anything, in a pointer-less language, pedagogically, 1-based is more intuitive for novices.
P.S. Classic VB and VBA had an odd feature where one could choose the base for themselves, using the Option Base feature.
1 reply →
I haven't tried Rhai and I wouldn't call myself anything more than a casual Lua user, but from a glance, these could be some reasons I see others using Rhai instead of Lua:
- Closer to Rust syntax/data structures, so easier if you already know Rust but don't know Lua
- Built-in serde (popular Rust de/serializer) support, if you need that
- Not sure if existing Rust<>Lua crates have it, but the debugging interface looks like the beginning of something useful (https://rhai.rs/book/engine/debugging/)
- Made in Rust, so again, if you already use Rust (which you do, if this crate is an option) it'll most likely to be easier to change for your needs, than modifying a Lua runtime
Personally, I'd probably still go for Lua rather than something like Rhai, if I had to choose something for algol-like scripting support. Otherwise I think Steel (https://github.com/mattwparas/steel) looks like the most useful scripting environment for Rust currently.
mlua has serde support
2 replies →
I’m already writing a Rust system. I’ve tried integrating with mlua. It works, but Rhai is simpler to embed. Simpler to build.
(I also tried Piccolo which is very cool but also not simple.)
Rhai also doesn’t include a lot of complexity that Lua does. It encourages you to write extension types in Rust, which is what I want.
Rhai doesn’t have GC, just refcounts. Rhai also can disable a lot of features, say if you just want an expression language.
I use it to write trading logic. I like that it’s stripped down and simple.
Thanks for these examples. I wrote a POC tool to apply common patches to multiple projects. These patches where supposed to be written as scripts. Idea was to run the same set of action on projects with different layouts based on their names etc. So a normal patch would not work.
Anyways I used mlua because it was the easiest way to get some scripting to run. But I faced issues writing the scripts. Basic operations had to come from the host like “string endsWith”, “list contains” and some other basic methods (Can’t remember which ones it where in detail). That mixed with the fact that Lua is so different. I knew I could not give this to any other dev in my team without a lot of instructions how lua handles stuff differently. So it’s nice to know I have a potential new goto solution when facing this again. Especially the ability to dumb it down.
> I’ve tried integrating with mlua. It works, but Rhai is simpler to embed. Simpler to build.
I'm curious what challenges you faced when embedding Lua via mlua? I've done it many times in projects and I've always found it to be trivial.
And then bringing it into Rust is simple like:
Are your requirements more complicated than this that makes it not as easy? I've never had to mess around with linking the system Lua, or anything else. mlua just brings its own Lua distribution with the `vendored` feature.
2 replies →
I've worked with moai (1) reasonably extensively, and the lua in it is not easy to use and sucks.
Specifically, a project that is composed entirely of lua with no other dependencies is indeed very easy to build and maintain. I agree.
However, my $0.02 would be that if you plan to have a large project with many 3rd party dependencies, then cmake, visual studio, C++, lua and the time spent jumping between them and maintaining those dependencies in a custom build toolchain will cost you more time and effort than the benefits that lua offers (2).
...and you do, indeed, need to do that, because c++ lacks a centralized package ecosystem and unified build tooling; and as operating systems change, existing builds stop working.
So, yes, you may consider my answer to be 'rust'; but the actual answer is 'not C++ and not cmake'.
That all said, lua is a more mature better system than this is currently, with good resources online and an active community. In cases where a small dependency tree can be maintained, it's still the best choice I'm aware of.
I'm simply pointing out that there are reasons you would pick it over lua, and I think they're quite compelling for cases where the future dependency graph of your project is unknown / unknown.
[1] - https://github.com/moai/moai-dev
[2] - ...and yes, I'm aware that moai is especially egregious in this regard. I get it.
"Not C++ and not cmake" is right on the money. Also Rhai seems to be in its infancy. Despite its current limitations, there's an argument for supporting it early to see where this road will take us. Hopefully to a relatively fast and more feature rich scripting language and engine that fit snuggly into Rust projects, which would be fantastic.
Also played around with moai back in the days when Doublefine had their Kickstarter and choose it as the base framework. But I found it hard to make the jump between c++ and lua etc. The docs made the decision when to write what simple. Everything lua until it becomes a bottleneck. But when to start from nothing this decision is still hard. Pull a lib and expose it to Lua or try to write the lib in Lua? Stuff like that. I needed a basic triangulation algorithm for 2d shapes and implemented that myself from a paper about the ear clipping algorithm. Fun times. All hobby stuff just for fun and games.
For one, I don't like the Lua language. This is just my opinion but I dislike the syntax and semantics. It's better than vimscript for configuring neovim but that's a pretty low bar.
And if the argument is "pick a long-supported standard as an embedded language" then I'd rather go with Lisp (e.g. https://github.com/mattwparas/steel). Historically, Lua is the fun hobby project newcomer.
Which features would compel you to use a language targeting roughly Lua's niche over Lua?
Thousands of devs vs some random lang
> memory-safety can be assumed already to be understood, but not considered compelling arguments
Depending on your application, memory safety could be a very compelling argument.
The Rhai interpreter is configurable, you can choose which features of Rain will be available on the runtime. You can even disable keywords.
Just like lua?
There are a bunch of crates available for scripting in Rust/Bevy (Rhai, Rune, Luau, Teal, etc), anyone who've tried specifically Rhai with specifically Bevy before and could share their experience?
Since a quick ctrl-f didn’t find any mention here or on the README:
I assume the name is a reference to ChaiScript, which is a similar embedded scripting language for C++.
https://github.com/ChaiScript/ChaiScript
My understanding is that the creator of Rhai, Sophia Turner, is one of the original creators of ChaiScript, along with her cousin Jason Turner
Very impressive. I read the readme and I’m unsure how memory management works, is it GC? And is it OOP or not? Thanks :)
Only guessing, but since the language is for scripting then maybe all garbage could be collected when the script finishes?
I was also curious. Looking at the code, it seems values are Boxed, but there's a special type called Shared that is an Rc-RefCell (unless Send-enabled.):
1 reply →
When I last read about Rhai it was apparently very slow such that it was simply faster and more ergonomic writing scripts in Rust itself, has that changed?
According to the documentation it evaluates by walking the ast, so yes, this is considered very slow. The readme also mentions 1 million loop iterations in 0.14s (140ms). Even my unoptimised naive lips like language [1] (also implemented via a ast walker) does the same in 28.67ms - so yes id consider this pretty slow.
[1]: https://github.com/xNaCly/Sophia
As someone who is also implementing a naive "walk the AST" evaluator for lisp, what would be considered OK/fast/not-slow in the case for 1 million loop iterations? Would ~30ms be considered fast or "not-slow"?
2 replies →
Not sure when you read this, but I can tell you that two years ago it was VERY slow. I used it for a game and I had to redo it in lua some months later because it was a big bottleneck. I don't have more up to date information.
Writing scripts in rust is as easy as copying and pasting this shebang: https://neosmart.net/blog/self-compiling-rust-code/
But that doesn’t make it embeddable, of course.
Nightly doesn't need 3rd party tools at all: https://doc.rust-lang.org/cargo/reference/unstable.html#scri...
1 reply →
I use it in rsmodules https://github.com/fretn/rsmodules and I’m very happy with my choice. Was easy to implement and the earliest versions already offered what I needed.
If the rhai author is reading this: thank you for this nice piece of software
What does "Passes Miri." mean? There is no link and online I only find a Malaysian city with that name.
Miri is a Rust tool with a similar function to Valgrind, it checks for undefined behavior.
https://github.com/rust-lang/miri
For anyone curious for more details, miri works in a different way than valgrind. it is an interpreter for rust that does additional checks at runtime to detect undefined behavior. This allows it to be fully deterministic, simulate other platforms, and do additional checks that I don’t think would be possible for valgrind
Someone else already answered, just a tip for future searches: If you know somewhat the context (in this case Rust), adding just one keyword to your query (in this case "Miri Rust") will give you the right answer as the first hit :)
This is awesome. Would this be a replacement for Lua use cases in a Rust desktop app?
In an Apollo GraphQL federation, you can use Rhai to customize the Apollo Router's behaviour. The Apollo Router is written in Rust.
That’s very cool! I appreciate how you can simplify interacting with rust applications without rust programming skills.
Needs a comparison to the excellent Rust Starlark Implementation.
Starlark is for config, not scripting.
I'm not sure what you think the difference is.
4 replies →
Cool! But wouldn't the way to go for scripting in a language these days to just compile to WASM and run in an embedded micro-VM? Why hasn't Rhai made this choice?
1. That require compiling to wasm, and for some use cases, you don't want a compilation step. That might even be a big part of why you are using a scripting language.
2. That requires an entire wasm runtime, which is a pretty heavy dependency
How does it compare to Rune and the others?