Embed in Rust
Add the runtime as a crate, instantiate a VM, hand it source. Foreign classes give Wren scripts a typed door into your Rust code.
Add the dependency
[dependencies]
wren_lift = { git = "https://github.com/wrenlift/WrenLift.git", features = ["host"] }
Pin a tag with tag = "v0.1.19" or a commit
with rev = "..." for reproducible builds.
Cargo features
| Feature | What it pulls in |
|---|---|
host (default) | Filesystem reads, plugin loading via libloading, the hatch installer. Drop for pure-embedded uses. |
aot | The Cranelift ObjectModule pipeline used by wlift --aot. Needed only if you call AOT from your own binary. |
cranelift | The JIT backend. On by default; opt out for an interpreter-only build. |
Run a script
use wren_lift::runtime::vm::{VM, VMConfig, ExecutionMode, InterpretResult};
let mut vm = VM::new(VMConfig {
execution_mode: ExecutionMode::Tiered,
..VMConfig::default()
});
vm.output_buffer = Some(String::new());
let result = vm.interpret("main", r#"
System.print("hello from rust")
"#);
assert!(matches!(result, InterpretResult::Success));
println!("script output: {}", vm.take_output());
The first argument to interpret is the module
name — it shows up in error backtraces and is the
import "modname" key from other modules.
Expose a foreign class
Foreign classes give Wren scripts a typed door into your
Rust code. Declare the Wren side with the
#!native attribute:
#!native = "myapp"
foreign class Counter {
#!symbol = "myapp_counter_new"
foreign static create(start)
#!symbol = "myapp_counter_next"
foreign static next(handle)
}
And the matching Rust exports:
use wren_lift::ffi::{Value, WrenVm};
#[no_mangle]
pub unsafe extern "C" fn myapp_counter_new(vm: *mut WrenVm) {
let start = wren_lift::ffi::slot(vm, 1).as_num().unwrap_or(0.0) as i64;
let handle = MY_COUNTERS.insert(start);
wren_lift::ffi::set_return(vm, Value::num(handle as f64));
}
#[no_mangle]
pub unsafe extern "C" fn myapp_counter_next(vm: *mut WrenVm) {
let handle = wren_lift::ffi::slot(vm, 1).as_num().unwrap_or(0.0) as usize;
let n = MY_COUNTERS.bump(handle);
wren_lift::ffi::set_return(vm, Value::num(n as f64));
}
The wlift_abi crate (re-exported through
wren_lift::ffi) provides the C-shaped surface
you import: slot reads an argument,
set_return writes the result,
alloc_string / alloc_list /
alloc_map create Wren-side objects.
Plugins (cdylib)
For dynamic loading (the same shape every
@hatch: plugin uses), build your crate as a
cdylib and ship it next to a hatchfile:
[lib]
crate-type = ["cdylib"]
[dependencies]
wlift_abi = { git = "https://github.com/wrenlift/WrenLift.git" }
Further reading
- Foreign-class reference — the Wren-side syntax in detail.
- wlift_sqlite source — a complete plugin example.
- wlift_abi/src/lib.rs — the full ABI surface plugins consume.