geam 0.2.3

Experimental Rust-embedded execution runtime for typed Gleam programs
Documentation

Geam

crates.io LICENSE CI docs.rs

Geam is a Rust runtime and embedding layer for Gleam.

Run a Gleam project, call Gleam functions from Rust, or connect a Gleam package to native Rust code. You keep writing Gleam and using Gleam packages; Geam looks after the Rust side.

The basic arrangement is similar to embedding Lua in a native application: Rust owns the process, while Gleam supplies statically typed application logic. Gleam source stays Gleam; Geam runs the type-checked program and generates the Rust runner or bindings that connect it to the host.

Try it

Geam runs the Erlang-compatible path of a Gleam project in its Rust runtime. Packages that use bodyless Erlang externals need matching Rust providers; JavaScript-only externals are unavailable in this workflow. See compatibility for the exact rules.

Geam requires Gleam v1.18.1, Rust 1.96 or newer, and a 64-bit Rust target. Install Geam with Cargo:

cargo install geam --locked

Run a Gleam project

Run an existing Gleam application on Geam's Rust runtime:

cd my_gleam_app
geam run

Geam prepares and maintains the project-local Rust runner while you continue working in Gleam. The standalone guide continues from here.

Call Gleam from Rust

To call Gleam functions from Rust, create a Rust application and initialize its nested Gleam project and generated bindings:

cargo new my_rust_app
cd my_rust_app
geam embedding init

geam embedding init adds a nested Gleam project and generated Rust bindings to the Cargo package. The starter module it creates contains this function:

// gleam/src/my_rust_app.gleam
pub fn double(value: Int) -> Int {
  value * 2
}

To call double from Rust, use this as src/main.rs:

mod geam_bindings;

use geam::embedding::ModuleBuilder;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let program = geam_bindings::project().compile()?;
    let builder = ModuleBuilder::from_program(program)?;
    let (bindings, functions) = geam_bindings::bind(builder)?;
    let module = bindings.seal();
    let mut echo = Vec::new();

    let value = module.call(&functions.double, (21.into(),), &mut echo)?;
    println!("{value}");
    Ok(())
}

cargo run now prints 42. The Rust embedding guide explains each part of this lifecycle. The executable embedding examples then add structured data, Gleam packages, IO routed through Rust, an external provider, and repeated calls one step at a time.

Add Rust to a Gleam package

Ordinary Gleam packages run without this extra step. When a function is implemented outside Gleam, the Gleam package still owns the public API and a companion Rust crate can provide its Geam implementation. Geam calls that crate a host provider.

The Gleam package declares the function:

// src/example_text_tools/casing.gleam
@external(erlang, "geam_example_text_tools_casing", "upper")
pub fn upper(value: String) -> String

@external marks the function as implemented outside Gleam. Geam follows the Erlang-compatible source path, but links this declaration to Rust instead of calling the named Erlang function.

The provider implements the same package, module, function, and signature in Rust:

#[geam::provider(
    package = "example_text_tools",
    modules = [casing],
)]
pub struct Component;

#[geam::module(path = "example_text_tools/casing")]
mod casing {
    use geam::provider::EcoString;

    #[geam::function]
    fn upper(value: EcoString) -> EcoString {
        value.to_uppercase()
    }
}

The application keeps using the Gleam module:

import example_text_tools/casing

pub fn main() {
  assert casing.upper("Geam") == "GEAM"
}

Geam links that call to the Rust implementation.

For automatic crates.io discovery, a provider for example_text_tools is named geam-example-text-tools or geam-example-text-tools-<suffix>. The name makes the crate discoverable; its metadata declares the Gleam package and versions it implements.

The Add Rust to a Gleam package guide follows one function from its Gleam declaration to Rust and then through geam run. Its provider names for automatic discovery section defines the crates.io naming rule.

Where Geam fits

Geam is an independent project that brings Gleam code into Rust-hosted applications. It complements Gleam's Erlang and JavaScript targets, which remain the natural choice when they fit how your application runs and deploys. Choose Geam when Rust needs to host the application, provide native capabilities, or call Gleam through generated bindings.

Geam uses Gleam's parser and type analysis to build executable plans for its Rust runtime. Generated Rust provides host integration: a managed standalone runner or typed embedding bindings.

A Gleam package keeps its Hex identity, source, and existing target implementations when it gains a Geam provider. The companion Rust crate adds a Geam implementation; it does not replace or translate the Gleam package.

Growing, but experimental

Geam is actively evolving toward a stable 1.0 API, so public APIs may still change. Geam currently supports everyday Gleam data, functions, imports, custom types, pattern matching, generics, official package integrations, native host providers, and nested ordinary data passed between Gleam and Rust.

See compatibility for the verified Gleam and package baselines, current limits, and platform requirements. See architecture for how Gleam analysis, Geam planning, Rust providers, and execution fit together.

Documentation

The Rust API reference is published on docs.rs. Repository documentation covers user workflows, execution contracts, and project development, while docs.rs documents individual Rust API items.

License

Geam is licensed under the Apache License, Version 2.0. See LICENSE.