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:
For some reason, clicking on that link automatically downloads a file named "Human Fart.wav"...
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.
I thought it was clearer this way.
But there is a new version of the website under construction:
It will be changed to the normal look.
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)
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.
Genuine question, as I don't have any prior experience embedding any scripting language into a Rust project.
The fact that Rhai builds with just 'cargo build' shouldn't be underestimated - a Rust project with all pure-Rust dependencies is much easier to maintain / support / distribute across a wide variety of hosts!
$ cargo add mlua --features lua54,vendored
Then mlua will statically build with the Lua sources it bundles itself and no need to link the system Lua or do anything other than "cargo build" like normal.`cargo cross` exists, which can help but it's really a kludge.
Zig has no problem using Lua (via the Ziglua wrapper) and cross-compilation is a single command away.
Rather than developing and increasingly insular (navel-gazing?) ecosystem, it might be better to work on the root problem.
> Rather than developing and increasingly insular (navel-gazing?) ecosystem, it might be better to work on the root problem.
Every language has its own development budget and must focus on what makes more sense. Zig has great cross-compiling story and C is first class, Rust has a stable language, a compiler that doesn't crash and emits readable error messages, one has to set its priorities.
The good thing with zig is that you can use the zig compiler within Rust project to cross-compile C code (cargo-zigbuild IIRC) so you can have a cake and eat it too.
In fact it's so much of a C problem that lots of people even cross-compile their pure C code with Zig, because Zig handles all of the C mess!
You can educate yourself here: https://andrewkelley.me/post/zig-cc-powerful-drop-in-replace...
Perhaps instead the more constructive path would have been to try and defend why one (cargo, rust) cannot do what another (zig) can.
So far, all I can infer is an acceptance of how compiling C code is hard and rather messy. I am no more uninformed on this than this literally being a daily fact of my paid life, but do go on, tell me how I've missed the point.
Even something like a `Vec<u64>` is likely to be a right pain with Lua.
If you are thinking in rust already, why would you go to an FFI c solution?
(Source: have been prototyping DSLs in rust with most of my life force for about a year)
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.
This doesn't make one better than the other, but precedent/familiarity does matter and represents a valid decision weight.
(I don't have a dog in this fight; I'm not a user of either Lua or Rhai.)
No one can convince me thst 0-based indexing is worth more than widespread editor syntax -highlighting support, working LSPs, lots of blog posts, books and more vs a new niche language whose marquee feature seems to be it is easier to embed in rust.
Again, I don't want to put down the authors, making languages and tooling is fun and awesome, but I wouldn't pretend this is a rstional replacement for Lua.
Lua is my first 1-indexed language, i have yet to have a single bug over this. It's a irrational fear.
But a vague, unsubstantiated feeling that one day, you might introduce a one-off indexing bug should not justify discarding Lua, and all its substantial benefits over project.
Arguing the pro's of Rust data-structure interop in case of a Rust project - that makes sense. This, this is basically nonsense.
Nobody said anything like that. There's no vague feeling of indexing bugs; there's a concrete degradation of code quality.
And yes it's only one factor, not a deal-breaker. Nobody here said it was.
It absolutely is. I gave you concrete reasons. (Edit: actually in a sibling comment)
> i have yet to have a single bug over this.
I never said anything about bugs.
Also I don't think Lua's LSP/type annotations are anything to boast about. They exist, but they aren't good. If you care about that there are way better choices - Typescript, or maybe Dart (not sure how embeddable that is though).
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.
Fundamentally either work, but 1-based indexing is a mistake mainly because it leads to much less elegant code.
A simple example is accessing rows in a flattened matrix (or 2D array).
With 0-based intervals and right-open intervals it is
array[stride*y .. stride*y + width]
With 1-based it is array[stride*(y-1)+1 .. stride*(y-1)+1+width]
Ouch. Code dealing with intervals is simpler with 0-based indexing & right-open intervals 99.9% of the time.Take a look at the example code here:
https://www.lua.org/pil/11.2.html
What index does the flattened `mt` start at?
> What index does the flattened `mt` start at?
I'm not sure if I missed a point you were getting at with this? Lua array table indexes start at 1.
You see that in books and papers and tutorials a lot. They'll explain some basic concepts nicely and then realise that they can't really explain ELI5 calculus and skip straight into Laplace transforms.
I once read a highly technical manual for a SystemVerilog simulator that began by explaining what double-clicking was. No joke. That's basically 1-based indexing.
The code example is
mt = {} -- create the matrix
for i=1,N do
for j=1,M do
mt[i*M + j] = 0
end
end
What index do I use to access the first element in mt? It isn't 1!If this was written using 0-based indices then the answer would be zero.
This could be argued to be a feature. That it isn't conflating the concepts but communicating both.
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.
- 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.
(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.
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'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 haven't used Rhai, but Lua has a lot of impedance mismatch with Rust that could be avoided with a fresh language. (Or maybe even just a fresh implementation of Lua, like piccolo is trying: https://github.com/kyren/piccolo)
Nothing was hard, but Rhai was easier.
If I were to write a system in my embedded trading strategy language, if I cared about extensibility and composition, maybe Lua’s complexity would begin to pay its way. But I’m not, I just want simple, hot-reloadable logic to drive some fast Rust code.
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.
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.
Depending on your application, memory safety could be a very compelling argument.
I assume the name is a reference to ChaiScript, which is a similar embedded scripting language for C++.
// 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.
There are no references as values are always cloned (or moved).
But that doesn’t make it embeddable, of course.
If the rhai author is reading this: thank you for this nice piece of software
But practically, the difference is intention, which drives design and ecosystem.
E.g: starlark is very oriented toward idempotence and limiting side effects to get reproducible config data. By default they discourage reading files: https://github.com/bazelbuild/bazel/issues/13300
But rhai is not particularly oriented toward config, and the doc promotes an extension to read files: https://rhai.rs/book/lib/rhai-fs.html
The tutorials, stdlib, language design anf tooling will all reflect this.
You probably don't want to use starlark to automate much action, but it will be well suited to describe states.
Take a look here: https://github.com/facebook/buck2-prelude
Also, you are confusing how bazel uses starlark with starlark itself. It's trivial to expose an open or read_file function.
2. That requires an entire wasm runtime, which is a pretty heavy dependency