Ion
A fast, embeddable scripting language for Rust applications.
Ion is designed for embedding in Rust programs — like Lua for game engines, but with Rust-flavored syntax and strong typing. It features a tree-walk interpreter and an optimizing bytecode VM, Serde-native host integration, a native Tokio async runtime, and a rich standard library.
Quick Start
use Engine;
Features at a Glance
// Variables — immutable by default
let name = "Ion";
let mut counter = 0;
// Destructuring
let (x, y) = (10, 20);
let [head, ...rest] = [1, 2, 3, 4];
// String interpolation
let greeting = f"hello {name}!";
// Functions with default args
fn greet(who, greeting = "hello") {
f"{greeting}, {who}!"
}
// Pattern matching
let result = Ok(42);
match result {
Ok(n) if n > 0 => f"positive: {n}",
Ok(n) => f"non-positive: {n}",
Err(e) => f"error: {e}",
}
// First-class functions
let double = |x| x * 2;
[1, 2, 3].map(double) // [2, 4, 6]
// Dicts (JSON-like)
let config = #{
host: "localhost",
port: 8080,
tags: ["web", "api"],
};
// Error handling
fn parse(s) {
let n = s.to_int()?;
Ok(n * 2)
}
// Try/catch
try {
risky_operation();
} catch e {
f"caught: {e}"
}
// Modules and imports
use math::{add, PI};
let area = add(PI, PI);
// Loops + functional
let sum = [1, 2, 3, 4, 5].fold(0, |acc, x| acc + x);
for (i, item) in enumerate(items) {
io::println(f"{i}: {item}");
}
Embedding in Rust
use Engine;
use Value;
let mut engine = new;
// Register host functions (plain fn pointer — no captures)
engine.register_fn;
// Register closures that capture host state (DB pool, shared counter, etc.)
let hits = new;
let hits_c = hits.clone;
engine.register_closure;
// Use from Ion scripts
let result = engine.eval.unwrap;
assert_eq!;
Register Modules
use Module;
use Value;
let mut math = new;
math.register_fn;
math.set;
engine.register_module;
// Scripts can use: math::add(1, 2) or `use math::*;`
With async-runtime, modules can expose native async host functions too:
let mut sensor = new;
sensor.register_async_fn;
engine.register_module;
let value = engine.eval_async.await?;
Host Types with Derive
use ;
use Value;
let mut engine = new;
engine.;
let result = engine.eval.unwrap;
Bytecode VM
let mut engine = new;
// Tree-walk interpreter (default)
let a = engine.eval.unwrap;
// Bytecode VM — faster for hot paths
let b = engine.vm_eval.unwrap;
assert_eq!;
Native Async Runtime
With the async-runtime feature, hosts can register Tokio-native async
functions. Ion scripts call them like ordinary functions; Engine::eval_async
parks the Ion continuation on the host future and resumes when Tokio wakes it.
No OS thread is spawned just to wait for network I/O.
use ;
use IonError;
async
Types
| Type | Example | Description |
|---|---|---|
| Int | 42, -1 |
64-bit signed integer |
| Float | 3.14 |
64-bit floating point |
| Bool | true, false |
Boolean |
| String | "hello", f"x={x}" |
UTF-8 string |
| List | [1, 2, 3] |
Ordered collection |
| Tuple | (1, "a", true) |
Fixed-size heterogeneous |
| Dict | #{key: val} |
Ordered key-value map |
| Option | Some(42), None |
Optional value |
| Result | Ok(val), Err(msg) |
Success or error |
| Bytes | b"\x00\xFF" |
Immutable byte array |
| Unit | () |
No value |
Project Structure
ion-core/ Core library — lexer, parser, interpreter, compiler, VM
ion-derive/ #[derive(IonType)] proc macro
ion-cli/ Script runner and REPL
ion-lsp/ Language server (VSCode support)
editors/ Editor syntax highlighting
Cargo Features
| Feature | Default | Description |
|---|---|---|
vm |
Yes | Bytecode compiler + stack VM with peephole optimization, constant folding, DCE, and TCO |
derive |
Yes | #[derive(IonType)] proc macro |
async-runtime |
No | Tokio-native async host functions, eval_async, spawn / .await / select, timers, channels |
legacy-threaded-concurrency |
No | Legacy sync-eval backend using OS threads and crossbeam channels |
obfuscate |
No | String obfuscation via obfstr |
msgpack |
No | Value::to_msgpack() via rmpv |
rewrite |
No | Source rewriter — rewrite::replace_global(src, name, new_value) |
Building
RUST_MIN_STACK=16777216
Documentation
- LANGUAGE.md — complete language specification
- DESIGN.md — design decisions and implementation phases
- docs/embedding.md — embedding API reference
- docs/concurrency.md — native async runtime, cancellation, channels, and the Tokio embedding pattern
- docs/stdlib.md — standard library reference
- docs/performance.md — performance strategy
- docs/vm-internals.md — bytecode VM internals
- docs/testing.md — test suite layout
- docs/tooling.md — CLI, LSP, editor support
Embedding inside a Tokio application
Enable async-runtime, register host futures with register_async_fn,
and call engine.eval_async(source).await. Native host futures are polled
by Tokio directly, so an Ion HTTP call can park without spawn_blocking,
Handle::block_on, or one OS thread per script task. See
docs/concurrency.md.
License
MIT