IRust is a Rust REPL that keeps state between expressions
Cross Platform Rust Repl
At a glance
- What is it?
- IRust is a cross platform Rust REPL written in Rust and published on crates.io, where each statement is compiled and run while earlier bindings stay alive, so you can poke at a value across lines instead of writing a program to print one. It adds dependency management, macro expansion, a debugger bridge and an async executor.
- Who is it for?
- IRust suits a Rust developer who wants to try an idea without a cargo project, since each line compiles and runs in place and earlier variables remain in scope, and the :add command removes the usual excuse that you need a manifest to pull in a crate.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 94 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A REPL that actually holds state
IRust is a cross platform Rust REPL, published on crates.io as irust and written in Rust, with the topics cross-platform, repl and rust.
The defining behaviour of a REPL is that state survives between lines, and IRust is built around that rather than around running each expression in a fresh process. That is the whole reason to use one instead of cargo script or a scratch binary: you type an expression, look at the type, then build on the value.
The command list in the README is long and worth reading as a feature list rather than a command reference, because it shows the shape of the tool. There is :type for showing an expression's type, :time for the amount of time an expression took to execute and :time_release for the same in release mode. There is :load to load a Rust file into the REPL and :reload for the last specified file. There is :pop to remove the last REPL code line and :del to remove a specific line by number, where the line count starts at 1 from the first expression statement.
There is also :cd to change the current working directory, which matters more than it sounds in a REPL that runs cargo underneath.
The editing commands are the other thing that separates a REPL from a shell. :edit opens the internal buffer in an external editor, using the editor from the EDITOR environment variable if none is given, and :sync then pulls the changes written by a GUI editor such as VS Code back into the REPL. The README notes that some GUI terminals require the :sync command after the edit, and names vscode as the case.
The release history shows a project that follows Rust closely: [email protected] and [email protected] both on 2026-07-23, and [email protected] on 2025-09-06. The version numbers track the Rust release they were built against, and the last push to the repository was on 2026-07-23.
Dependencies without a manifest
The :add command is the feature that makes IRust usable for exploration rather than just for arithmetic on numbers.
:add <dep_list> adds dependencies and it also accepts most cargo add arguments, for example you can import local dependencies with :add --path path_to_crate. That second form is the interesting one, because it means a REPL session can pull in a crate from the directory you are sitting in.
Without it, every REPL for a language with a package ecosystem is hobbled: you cannot try an API from a library you have not already committed to a manifest, so you end up creating a project, adding the dependency, and writing a main function to print one value. :add removes that loop.
The same applies to :check_statements, which is described as: if it is set to true, irust will check each statement, meaning input that ends with a semicolon, with cargo check before inserting it to the REPL. So the behaviour of a type error in a half-typed line is configurable rather than a fixed cost, and the default config shows check_statements = true.
The default configuration block in the README is worth reading in full, because it is the actual control surface, and this part of it is where the Rust integration and the general behaviour live:
# Rust analyzer
ra_inline_suggestion_color = "Cyan"
ra_suggestions_table_color = "Green"
ra_selected_suggestion_color = "DarkRed"
ra_max_suggestions = 5
enable_rust_analyzer = false
# other
first_irust_run = false
toolchain = "stable"
check_statements = true
auto_insert_semicolon = trueBeyond the check option, it has compile_time = false, which when on makes IRust print compiling time on each input, with the note that compile time includes rustc compiling plus some IRust code and that this should be marginal. It has compile_mode for debug or release, and compile_mode together with :time_release is what makes the timing command meaningful.
The prompts are configurable too, with input_prompt = "In: " and output_prompt = "Out: ", and the evaluator is a two element list, by default a println call with the debug format specifier followed by the input code, which is the wrapper that turns the value of your expression into the printed output.
Async, the evaluator, and the executor choice
Two settings decide what a REPL session can do, and both are worth understanding before you start typing.
The first is the executor. The :executor <executor> command sets the executor to be used by IRust, with available options sync, tokio and async_std, and the README states that by using an async executor, await becomes usable with no other modifications for async executors. The default configuration has executor = "Sync", so a fresh session is synchronous and await will not compile until you switch.
The second is the evaluator, which is the wrapper applied to each expression. The :evaluator <evaluator> command sets the evaluator statement, with the example :evaluator println!("{}",{$$}) where the $$ will be replaced by IRust with the input code. The default evaluator uses debug formatting, and :evaluator reset puts it back.
Changing the evaluator is a bigger lever than it looks. The default wrapper is a println with {:?}, so what you see is the Debug representation of the value. Switching to {} gives you the Display representation, which for most types is what a person actually wants to read. A custom evaluator can do more, and because the wrapper is a Rust snippet with a placeholder, you can bind the value to a variable inside the wrapper and print whatever you want about it.
The main_result option is the third piece and it is about ergonomics rather than output. Its available options are Unit and Result, where Result is Result<(), Box<dyn std::error::Error>>, and the README explains that using Result as type allows you to use ? in the REPL without any boilerplate. The default is Unit.
There is also a replace_marker = "$out" setting with replace_output_with_marker = false, described as using the last output by replacing the specified marker, which is a way to feed the previous result into the next expression.
Shell escapes and the script layer
IRust is not sealed off from the shell, and the two escape hatches are documented separately because they behave differently.
The :: prefix runs a shell command, for example ::ls. The $$ syntax interpolates shell commands into Rust code, for example let a = $$ls -l$$;, and this feature can be enabled or disabled via the config file. The README does not say which way it defaults, so the config file is the place to check.
The distinction is worth internalising. ::ls is a shell command whose output the REPL shows, and it is not Rust code. $$ls -l$$ is Rust code that happens to contain the result of a shell command, so the output becomes part of a Rust expression and has to type check as whatever that expression expects. The first is a debugging convenience, the second is a way to bring command output into your program's data.
The scripting feature is the third layer, and it is off by default with activate_scripting = false in the config. The :scripts command lists detected scripts when invoked with no arguments, prints a script's info if it exits, and with two arguments tries to activate or deactivate a script, with the example :scripts Vim deactivate. The repository carries a SCRIPTS.md file at the root and a script_examples/ directory, so the scripting surface is documented separately from the README.
There is also a :toolchain command to switch between toolchains, with supported values stable, beta, nightly and default, and the default config has toolchain = "stable". Given that the crate versions track Rust releases, that command is a way to test your code against a newer compiler without leaving the session.
Tooling that leans on the surrounding Rust ecosystem
Several commands are thin wrappers around other tools in the Rust ecosystem, and the README is explicit about the requirement in each case.
:asm shows the assembly of the specified function, and the conditions are spelled out: the function needs to be public, and there has to be no free standing statements or expressions. It requires cargo-show-asm, with a link.
:expand shows the result of macro expansion, the function argument is optional, and the example given is a function b that prints 42, then :expand b. It requires cargo-expand, linked at dtolnay/cargo-expand.
:dbg spawns rust-lldb or rust-gdb with an optional expression, for example :dbg or :dbg fact(12), and the debugger can be specified in the config file.
:bench runs cargo bench.
:show shows the REPL's current code, and it optionally depends on rustfmt to format the output. That last dependency is worth noting because it is the one that improves rather than extends the session, and it means a missing rustfmt costs you readable output rather than a feature.
The completion story is also ecosystem-dependent. To enable completion with tab via rust-analyzer, set enable_rust_analyzer to true in the config, and the default is false. The config also holds the rust-analyzer display settings, with ra_inline_suggestion_color, ra_suggestions_table_color, ra_selected_suggestion_color and ra_max_suggestions = 5.
So IRust integrates by delegating: the assembly, the expansion, the debugging and the completion are all provided by tools that have to be installed separately, and IRust's job is to call them with the right arguments and show the result.
Keybindings, scripts and the parts that are not Rust
IRust is a line editor as well as a REPL, and the keybindings are the other half of the interface.
ctrl-l clears the screen and ctrl-c clears the line. ctrl-d exits if the buffer is empty, which is the readline convention for end of input. ctrl-z, on unix only, sends IRust to the background. ctrl-r searches history, hitting ctrl-r again continues searching the history backward, and ctrl-s searches the history forward.
ctrl-left and ctrl-right jump through words, and HOME and END go to line start and line end. Tab and ShiftTab cycle through completion suggestions. Alt-Enter and ctrl-s add a line break, which is how you write a multi-line expression without leaving the prompt. ctrl-e forces evaluation.
There is a macro recorder built in, which is unusual for a language REPL. ctrl-o followed by a key starts recording a macro and it is saved on the specified key, and clicking ctrl-o again stops the recording. ctrl-p followed by a key plays a macro saved on that key. ctrl-u undoes and ctrl-y redoes, and ctrl-x deletes the current line.
A few environment-level details complete the picture. You can use the arrow keys to cycle through command history. You can disable all colors by setting the NO_COLOR environment variable, which is the standard convention and is worth noting because a REPL that emits colour codes into a pipe is annoying to work with. And if input is piped to IRust it will evaluate it and exit, with the example given as echo '"hello".chars()' | irust, which is the non-interactive mode and makes IRust scriptable.
The CLI has two options of its own: --help prints the help message and --reset-config resets the IRust configuration to default.
Where IRust fits, and where it stops
The honest comparison is with the alternatives for the same job, and the differences are about where the state lives.
The obvious alternatives are cargo script, which runs a single file as a program with dependencies declared in a comment block, and a scratch cargo project, which is the same idea with more ceremony. Both of those execute the whole file, so you cannot keep a value from line three and use it on line forty. That is the gap IRust fills, and it is a real one when you are exploring an API rather than executing a known program.
A language-agnostic REPL server, of which there are several, offers the same stateful evaluation but ties you to their own execution model. IRust instead drives cargo and rustc underneath, so the code you evaluate is compiled by the same compiler, with the same edition, that your project uses. The cost is that every statement is a compile, which is why the :time command and the compile_time setting exist at all, and why :compile_mode can switch to release when you want representative numbers.
The limits worth knowing are these. The evaluator wrapper means the default output is the Debug representation, and a custom evaluator is how you change that. The async support requires choosing an executor, and it is off by default. The advanced tooling requires external binaries. The scripting feature is off by default. And the version numbers follow Rust releases, so a session built against one compiler is not automatically valid against the next.
The Gitpod option is there for evaluation. The README says you can try out IRust with no installation or setup, via Gitpod.io, by visiting the repository URL through gitpod.io, and the repository carries a .gitpod.yml at the root to support that. It is the fastest way to decide whether the workflow suits you before installing anything.
Editorial conclusion
IRust suits a Rust developer who wants to try an idea without a cargo project, since each line compiles and runs in place and earlier variables remain in scope, and the :add command removes the usual excuse that you need a manifest to pull in a crate. It is the wrong tool if you want to benchmark or profile real code, because the evaluator wraps each expression in a println and the timings include that wrapper, and it is the wrong tool if you need a stable toolchain, because the version numbers track the Rust release and the :toolchain command can switch you to nightly mid-session. Before adopting it, install with cargo install irust, set executor to the async runtime you use if you intend to await, and enable enable_rust_analyzer only if you want tab completion.
Frequently asked questions
How do I install IRust?
The crate is published on crates.io as irust, so it is installed with cargo install irust. The repository also carries a .gitpod.yml so you can try it in a browser with no installation or setup, and the --reset-config CLI option resets the configuration to its defaults.
How do I add a crate dependency to an IRust session?
Use the :add command, which adds dependencies and accepts most cargo add arguments. The README notes you can import local dependencies with :add --path path_to_crate, which lets a session pull in a crate from the directory you are working in.
How do I use async code in IRust?
Set the executor with the :executor command, choosing sync, tokio or async_std. By using an async executor, await becomes usable with no other modifications, and the default configuration has executor set to Sync.
What is the IRust config file location?
On Linux it is /home/$USER/.config/irust/config.toml, on Windows C:\Users\$USER\AppData\Roaming/irust/config.toml, and on macOS /Users/$USER/Library/Application Support/irust/config.toml. The README lists the default values for the history, colour, rust-analyzer and other settings.
Which external tools does IRust need for its advanced commands?
The :asm command requires cargo-show-asm and the function must be public with no free standing statements or expressions, :expand requires cargo-expand, and :dbg spawns rust-lldb or rust-gdb. The :show command optionally depends on rustfmt to format the output.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sigmasd-irust)Was this useful?