Interpreter Mode
In interpreter mode (cljrs run / cljrs repl), the Rust crate is compiled to
a shared library and loaded at startup via dlopen. No recompilation of
cljrs itself is required.
Workflow
# 1. Build the shared library (once, or after Rust changes)
cljrs build-native
# 2. Run Clojure code — the .so is loaded automatically
cljrs run src/main.cljrs
# 3. Start the REPL — also auto-loads
cljrs repl
cljrs build-native is just cargo build run inside the crate directory
declared by :rust :crate in cljrs.edn. It inherits your normal Rust
toolchain, RUSTFLAGS, and cargo configuration.
How auto-loading works
When cljrs run (or repl) starts:
- It locates
cljrs.ednby walking up the directory tree. - If
:rustis present, it derives the library path from the crate name and profile (debugby default):- Linux:
<crate_dir>/target/debug/lib<crate_name>.so - macOS:
<crate_dir>/target/debug/lib<crate_name>.dylib - Windows:
<crate_dir>/target/debug/<crate_name>.dll
- Linux:
- It opens the library with
dlopenand looks up the symbol named after the last::segment of:rust :init(e.g."cljrs_init"). - It calls the symbol with a
*mut Registry, which registers all native functions into the global namespace table. - The library is kept loaded for the lifetime of the process.
All of this happens before any Clojure source is evaluated, so native functions
are visible to ns/require forms and to top-level code.
Missing library
If the shared library does not exist yet, cljrs prints a warning and
continues:
cljrs: native library not found at target/debug/libmy_project.so
— run `cljrs build-native` first
Clojure code that calls unregistered native functions will get a runtime error;
code that doesn’t call them runs normally. This is intentional: you can develop
pure-Clojure parts of a mixed project without running cljrs build-native on
every edit.
Release builds
Pass --release to build an optimised library:
cljrs build-native --release
The release library is placed at target/release/libmy_project.so (and so on).
Note that cljrs run always looks for the debug build; to use the release
library you currently need to copy it to the debug path or symlink it. (A future
--release flag on run/repl will automate this.)
Development tips
- Fast iteration:
cljrs build-nativecompiles only the Rust crate, not the whole clojurust workspace. On a warm build cache it typically takes a few seconds. - Separate processes: Because the library is opened once at startup,
changes to Rust code require restarting
cljrs run. There is no hot-reload of native code within a single process. - Cargo features: Pass
CARGO_FLAGSor set[profile.*]in yourCargo.tomlas usual;cljrs build-nativeinherits the environment. - Debugging: Use
RUST_LOG,RUST_BACKTRACE=1, and your normal Rust debugging tools. The library is a standard shared object;gdb/lldbcan attach to thecljrsprocess and set breakpoints in native code.