luars
A Lua 5.5 interpreter written in pure Rust — embeddable, async-capable, with derive macros for UserData.
Features
- Lua 5.5 — full language semantics: compiler, register-based VM, GC
- Pure Rust — no C dependencies, no
unsafeFFI - Ergonomic API — typed-first
call,call1,call_global,call1_global, typed callback registration,TableBuilder, typed getters - UserData — derive macros to expose Rust structs/enums to Lua (fields, methods, operators)
- Async — run async Rust functions from Lua via transparent coroutine bridging
- Closures — register Rust closures with captured state as Lua globals
- FromLua / IntoLua — automatic type conversion for seamless Rust ↔ Lua interop
- Optional Serde — JSON serialization via
serde/serde_json(feature-gated) - Optional Sandbox — feature-gated env isolation, injected globals, and runtime limits
- Standard Libraries —
basic,string,table,math,io,os,coroutine,utf8,package, partialdebug
Usage
[]
= "0.12"
# With JSON support:
= { = "0.12", = ["serde"] }
# With sandbox support:
= { = "0.12", = ["sandbox"] }
Basic Example
use ;
use SafeOption;
Register Rust Functions
vm.register_function_typed?;
let result: i64 = vm.call1_global?;
assert_eq!;
Raw callbacks remain available when you want direct stack access:
vm.register_function?;
vm.execute?; // Hello, Rust!
Load, Dofile, Call
// Compile without executing
let f = vm.load?;
let result: i64 = vm.call1?;
// Load named source
let f = vm.load_with_name?;
// Raw fallback when you already have LuaValue vectors
let results = vm.call_raw?;
// Execute a file
vm.dofile?;
TableBuilder
use TableBuilder;
let config = new
.set
.set
.push
.build?;
vm.set_global?;
// Iterate
for in vm.table_pairs?
let len = vm.table_length?;
Rust Closures
use ;
let counter = new;
let counter_clone = counter.clone;
let func = vm.create_closure?;
vm.set_global?;
UserData
use ;
vm.?;
vm.execute?;
Use UserDataRef<T> when Rust callbacks need typed access to userdata values:
vm.register_function_typed?;
Error Handling
use LuaError;
match vm.execute
LuaError is a 1-byte enum (Runtime, Syntax, OutOfMemory, MessageHandler, IndexOutOfBounds). For rich errors with messages and std::error::Error impl, use vm.into_full_error() → LuaFullError.
Selective Standard Libraries
use Stdlib;
vm.open_stdlib?; // everything
vm.open_stdlibs?; // specific set
Async
vm.register_async_typed?;
let results = vm.execute_async.await?;
If you need full manual control, register_async still accepts and returns raw LuaValue/AsyncReturnValue vectors.
Known Limitations
- UTF-8 Only Strings — unlike C Lua, strings must be valid UTF-8. Use
string.pack/string.unpackfor binary data. - Custom Bytecode Format —
string.dumpoutput is not compatible with C Lua bytecode. - No C API — pure Rust; cannot load C Lua modules.
- Partial Debug Library —
debug.sethookis a stub;getinfo/getlocal/tracebackwork. - No string-to-number coercion in arithmetic —
"3" + 1raises an error.
Documentation
| Document | Description |
|---|---|
| Guide | VM, execution, values, functions, errors, API reference |
| UserData Guide | Derive macros, fields, methods, constructors |
| Async Guide | Async Rust functions, architecture, HTTP server example |
| Differences | Behavioral differences from C Lua 5.5 |
License
MIT — see LICENSE.