rustowl Documentation

Visualize Ownership and Lifetimes in Rust

5,239 stars Rust Rust 14 files ยท ~15,079 tokens #lifetime#ownership#rust#visualization
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy rustowl on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name rustowl --restart always cordx56/rustowl:latest

Technical Documentation: cordx56/rustowl

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

RustOwl visualizes ownership movement and lifetimes of variables.
When you save Rust source code, it is analyzed, and the ownership and lifetimes of variables are visualized when you hover over a variable or function call.

RustOwl visualizes those by using underlines:

  • ๐ŸŸฉ green: variable's lifetime
    • definitely live: the variable is provably initialized on every path reaching this point
    • maybe live (wavy): the variable is initialized on some paths but may have been moved, dropped, or be uninitialized on others
  • ๐ŸŸฆ blue: immutable borrowing
  • ๐ŸŸช purple: mutable borrowing
  • ๐ŸŸง orange: value moved / function call
  • ๐ŸŸฅ red: lifetime error
    • Diff of lifetime between actual and expected, or
    • Invalid overlapped lifetime of mutable and shared (immutable) references

Detailed usage is described here.

Currently, we offer VSCode extension, Neovim plugin and Emacs package.
For these editors, move the text cursor over the variable or function call you want to inspect and wait for 2 seconds to visualize the information.
We implemented LSP server with an extended protocol.
So, RustOwl can be used easily from other editor.

Table Of Contents

Support

If you're looking for support, please consider checking all issues, existing discussions, and starting a discussion first!

Also, you can reach out to us on the Discord server provided above.

Quick Start

Here we describe how to start using RustOwl.

Prerequisite

  • cargo installed
    • You can install cargo using rustup from this link.
  • Visual Studio Code (VS Code) installed

We tested this guide on macOS Sequoia 15.3.2 on arm64 architecture with VS Code 1.99.3 and cargo 1.90.0.

VS Code

You can install VS Code extension from this link.
RustOwl will be installed automatically when the extension is activated.

For more detailed configuration options, see the VS Code Configuration Guide.

Vscodium

You can install Vscodium extension from this link.
RustOwl will be installed automatically when the extension is activated.

After installation, the extension will automatically run RustOwl when you save any Rust program in cargo workspace.
The initial analysis may take some time, but from the second run onward, compile caching is used to reduce the analysis time.

Same as VS Code, see the VS Code Configuration Guide for more detailed configuration options.

Other editor support

We support Neovim and Emacs.
You have to install RustOwl before using RustOwl with other editors.

You can also create your own LSP client.
If you would like to implement a client, please refer to the The RustOwl LSP specification.

Neovim

Minimal setup with lazy.nvim:

lua
{
  'cordx56/rustowl',
  version = '*', -- Latest stable version
  build = 'cargo install rustowl',
  lazy = false, -- This plugin is already lazy
  opts = {},
}

For comprehensive configuration options including custom highlight colors, see the Neovim Configuration Guide.

Emacs

Elpaca example:

elisp
(elpaca
  (rustowl
    :host github
    :repo "cordx56/rustowl"))

Then use-package:

elisp
(use-package rustowl
  :after lsp-mode)

You have to install RustOwl LSP server manually.

For more detailed configuration options, see the Emacs Configuration Guide.

RustRover / IntelliJ IDEs

There is a third-party repository that supports IntelliJ IDEs.
You have to install RustOwl LSP server manually.

Sublime Text

There is a third-party repository that supports Sublime Text.

Installation

Please see Installation for detailed installation instructions.

Usage

Please see Usage for detailed usage instructions.

Note

We support the 5 latest versions of Rust (best effort).

In this tool, due to the limitations of VS Code's decoration specifications, characters with descenders, such as g or parentheses, may occasionally not display underlines properly.
Additionally, we observed that the println! macro sometimes produces extra output, though this does not affect usability in any significant way.


Overview (`scripts/README.md`)

RustOwl Scripts

This directory contains utility scripts for local development and testing that complement the CI workflows.

Quick Start

bash
# Run development checks
./scripts/dev-checks.sh

# Check binary size changes
./scripts/size-check.sh

# Run security and memory safety tests
./scripts/security.sh

# Run performance benchmarks
./scripts/bench.sh

# Run Neovim Tests
./scripts/run_nvim_tests.sh

Script Overview

`dev-checks.sh`

Runs comprehensive development checks including formatting, linting, and tests.

Features:

  • Code formatting checks (cargo fmt)
  • Linting with Clippy
  • Unit tests
  • Integration tests
  • Documentation tests

`size-check.sh`

Analyzes binary size changes to detect bloat and track optimization efforts.

Features:

  • Binary size comparison
  • Dependency analysis
  • Size regression detection

๐Ÿ›ก๏ธ `security.sh`

Comprehensive security and memory safety testing framework.

Features:

  • Multi-tool testing (Miri, Valgrind, ThreadSanitizer, cargo-deny, cargo-shear, Instruments)
  • Cross-platform support (Linux, macOS, ARM64)
  • Graceful degradation when tools unavailable
  • Configurable test categories
  • Color-coded output with progress indicators

Usage:

bash
# Run all applicable tests for the current platform
./scripts/security.sh

# Run specific test categories
./scripts/security.sh --no-miri
./scripts/security.sh --no-valgrind
./scripts/security.sh --no-audit

# ThreadSanitizer is opt-in: it instruments every build, so it is slow
./scripts/security.sh --thread-sanitizer

# Check available tools and configuration
./scripts/security.sh --check

Platform notes:

  • On Linux, Instruments is disabled and Valgrind covers memory errors.
  • On macOS, Valgrind is disabled and a Time Profiler trace is captured with
    xcrun xctrace instead. The trace is written as
    instruments_output_<timestamp>.trace, which CI uploads as an artifact when
    the run fails.
  • ThreadSanitizer requires a nightly toolchain and is opt-in on any platform
    via --thread-sanitizer. CI exercises it in a dedicated Linux job. It needs
    rust-src, because -Zsanitizer changes the
    crate ABI and therefore requires -Zbuild-std to rebuild core and
    compiler_builtins with the same flag โ€” without it rustc fails with
    "mixing -Zsanitizer will cause an ABI mismatch".
  • Findings matching .tsan-suppressions are
    suppressed. They are not real races: any program building a multi-threaded
    tokio runtime reproduces them when the synchronisation sits inside
    OnceLock/Mutex or is driven through epoll, and tokio's own suite is clean
    under -Zbuild-std. The file records the evidence and what would make each
    entry safe to remove.
  • The instrumented build and the instrumented run are two separate steps. An
    aborted run produces zero ThreadSanitizer warnings and would otherwise be
    indistinguishable from a clean one, so a failed build fails the suite rather
    than reporting "no races detected".
  • The run is bounded at 300s, because a TSan run does not always converge โ€”
    tokio's test_tuning in rt_threaded.rs has to be killed after ~27 minutes
    under the sanitizer.
  • cargo-shear runs on both Linux and macOS.
  • In CI, missing tools are installed automatically unless you pass
    --no-auto-install, which is honoured even though CI detection would
    otherwise enable installation.

Dependency policy: `cargo-deny`

cargo-deny checks the
dependency graph for known advisories, disallowed licences, banned crates and
unexpected sources. Its rules live in deny.toml at the repo
root; cargo deny check must pass locally before pushing.

bash
cargo deny check                     # all four checks
cargo deny check advisories          # just the vulnerability check

CI runs this in a separate job via
EmbarkStudios/cargo-deny-action,
so security.sh is invoked there with --no-audit to avoid resolving the full
graph three times across the OS matrix.

Notes on the current configuration:

  • multiple-versions is a warning: duplicate versions are normal in a real
    graph and unifying them is rarely worth the churn.
  • MPL-2.0 is not in the blanket allow list. rustowl is MPL-2.0, and it is
    permitted by name, so adding another MPL-2.0 dependency has to be a deliberate
    edit rather than an accident.
  • Other per-crate exceptions exist for r-efi (LGPL-2.1-or-later),
    webpki-root-certs (CDLA-Permissive-2.0) and libbz2-rs-sys
    (bzip2-1.0.6), each with a comment explaining why.

Unused dependencies: `cargo-shear`

cargo-shear replaces cargo-machete.
security.sh runs it with --check-test-targets, so a dependency used only
from a test target is not reported as unused.

bash
cargo shear --check-test-targets

๐Ÿ“Š `bench.sh`

Local performance benchmarking with regression detection.

Features:

  • Criterion benchmark integration
  • Baseline creation and comparison
  • Automatic test package detection
  • Configurable regression thresholds
  • HTML report generation
  • Local development focus

Usage:

bash
# Standard benchmark run
./scripts/bench.sh

# Create and compare baselines
./scripts/bench.sh --save my-baseline
./scripts/bench.sh --load my-baseline --threshold 3%

# Development workflow
./scripts/bench.sh --clean --open --test-package ./examples

Note: Benchmarks are designed for local development only. CI environments introduce too much variability for reliable performance measurement.

`run_nvim_tests.sh`

Run tests defined in nvim-tests directory.

Usage

bash
./scripts/run_nvim_tests.sh

In uses [mini.test](https://github.com/echasnovski/mini.test) plugin to test.

Prerequisites

Common Requirements

  • Rust toolchain (automatically managed via rust-toolchain.toml)
  • Basic build tools
  • Neovim (Only For The Tests And If You Are Changing Neovim Specific Things)

Platform-Specific Tools

Linux

bash
sudo apt-get update
sudo apt-get install -y valgrind gnuplot build-essential

macOS

bash
brew install gnuplot
# Optional: brew install valgrind (limited support)

Integration with CI

These scripts are designed to complement CI workflows where applicable:

  • security.sh โ†” .github/workflows/security.yml: Same security analysis tools

Note: Benchmarking (bench.sh) is intentionally local-only due to CI environment variability.

GitHub Actions Integration

The scripts integrate with workflows where appropriate:

Security Workflows

  • security.yml: Runs comprehensive security testing across platforms

Development Scripts

  • bench.sh: Local-only performance testing (not suitable for CI)
  • dev-checks.sh: Can be used in CI for code quality checks

Development Workflow

Before Committing

bash
# Run all development checks
./scripts/dev-checks.sh

# Run security tests
./scripts/security.sh

# Check performance impact (local only)
./scripts/bench.sh

# Check binary size impact
./scripts/size-check.sh

# If you changed any neovim specific things (like lua files)
./scripts/run_nvim_tests.sh

Setting Up New Environment

For setting up a development environment, ensure you have the platform-specific tools listed in the Prerequisites section above.

Troubleshooting

Script Permissions

bash
chmod +x scripts/*.sh

Missing Tools

Run the setup script or check platform-specific installation commands above.

CI Failures

  • Check workflow logs for specific error messages
  • Verify rust-toolchain.toml compatibility
  • Ensure scripts have execution permissions
  • Test locally with the same script used in CI

Script Architecture

All scripts follow common patterns:

  • Color-coded output with emoji indicators
  • Progressive enhancement based on available tools
  • Comprehensive help text with examples
  • Error handling with remediation suggestions
  • Cross-platform compatibility with platform-specific optimizations

Shared code

lib/common.sh holds everything the in-repo scripts have in common: the colour
constants and log_* helpers, the NixOS nix-ld path setup, have_cmd /
require_cmd, print_host_tuple, the rustc version gate, and the integer
percentage helpers. Scripts source it as lib/common.sh relative to their own
directory:

sh
SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname "$0")" && pwd)"
. "$SCRIPT_DIR/lib/common.sh"

The library is POSIX sh โ€” no [[ ]], no arrays, no echo -e โ€” because
toolchain sources it and runs under dash and busybox-ash in CI. It also has no
side effects at source time.

installer is the one exception. It is piped into sh from a URL and so has
to stay self-contained; it keeps its own copy of print_host_tuple.


Overview (`vscode/README.md`)

RustOwl VSCode

This is RustOwl extension for VSCode. It will download the binaries and will do toolchain setup automatically.

Configuring RustOwl

For detail, refer to the vscode configuration guide.


Overview (`crates/rustowl/perf-tests/dummy-package/README.md`)

RustOwl Performance Test Dummy Package

This is a dummy Rust package specifically designed for performance testing with RustOwl. The package contains various Rust patterns and constructs that RustOwl can analyze, including:

Features Tested

Ownership and Borrowing Issues

  • Methods returning references to internal data
  • Potential lifetime issues
  • Clone operations that might be unnecessary

Error Handling Patterns

  • Use of unwrap() that might panic
  • Proper error handling with Result and Context
  • Missing error propagation

Resource Management

  • File handling and potential resource leaks
  • Thread safety and potential deadlocks
  • Memory allocation patterns

Async/Concurrency Patterns

  • Async operations with tokio
  • Thread-based parallel processing
  • Shared state with Arc<Mutex<T>>

Performance Patterns

  • Memory-intensive operations
  • CPU-intensive computations
  • I/O operations

Usage

The dummy package can be built and run to provide realistic workload for RustOwl analysis:

bash
cargo build --release
cargo run -- --help

Operations

The dummy application supports several operations:

  • data - Run data structure operations with potential ownership issues
  • network - Run network operations (may fail without internet)
  • files - Run file I/O operations
  • compute - Run CPU-intensive computations
  • all - Run all operations

Performance Testing

This package is used by the RustOwl performance testing script (perf-tests.sh) to provide a consistent, realistic codebase for performance analysis.

The code intentionally contains patterns that RustOwl can analyze and potentially flag, making it an ideal test case for measuring RustOwl's analysis performance on real-world-like Rust code.

Testing Modes

The performance testing script supports different analysis modes:

  • Default Mode: Fast analysis with current target and default features
  • Comprehensive Mode: Thorough analysis with --all-targets --all-features

The package includes multiple targets (main binary, examples, tests) and conditional features to enable comprehensive testing of RustOwl's analysis capabilities across different code paths and compilation scenarios.


CODE OF CONDUCT (`docs/CODE_OF_CONDUCT.md`)

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes,
    and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall
    community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of
    any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address,
    without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a
    professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[email protected].
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of
actions.

Consequence: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including
sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the
community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant,
version 2.1, available at
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by
Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


CONTRIBUTING (`docs/CONTRIBUTING.md`)

Contribution guide

This document is under construction.

Thank you for considering to contribute RustOwl!

In this document we describe how to contribute our project, as follows:

  • How to setup development environment
  • Checklist before submitting PR

Table of Contents

Set up your environment

Prerequisites

Common Requirements

  • Rust toolchain (automatically managed via rust-toolchain.toml)
  • Basic build tools

Platform-Specific Tools

Linux

bash
sudo apt-get update
sudo apt-get install -y valgrind bc gnuplot build-essential # For Rustowl Itself
sudo apt-get install -y neovim # Optional: Install Neovim for neovim plugin development
sudo apt-get install -y emacs # Optional: Install Emacs for emacs plugin development
# Install Visual Studio Code as in their documentation

macOS

bash
brew install gnuplot
# Optional: brew install valgrind (limited support)
brew install neovim # Optional: Install Neovim for neovim plugin development
brew install emacs # Optional: Install Emacs for emacs plugin development
# Install Visual Studio Code as in their documentation

Rust code

In the Rust code, we utilize nightly compiler features, which require some tweaks.
Before starting this section, you might be required to install rustup since our project requires nightly compiler.

Our project uses rust-toolchain.toml to automatically manage the correct Rust version.

Build and test using the nightly environment

For building, testing, or installing, you can do the same as any common Rust project using the cargo command.

Build with stable Rust compiler

To distribute release binary, we use stable Rust compiler to ship RustOwl with stable Rust compiler for users.

The executable binary named rustowlc, which is one of the components of RustOwl, behaves like a Rust compiler.
So we would like to compile rustowlc, which uses nightly features, with the stable Rust compiler.

[!NOTE]
Using this method is strongly discouraged officially. See Unstable Book.

To compile rustowlc with stable compiler, you should set environment variable as RUSTC_BOOTSTRAP=1.

Our script automates most of the work, so to build with the toolchain specified in channel file:

bash
./scripts/build/toolchain cargo build --release

To do it manually:

bash
# 1.89.0 can be any version
RUSTC_BOOTSTRAP=1 rustup +1.89.0 run cargo build --release

As new Rust versions release, their APIs change. Thus, RustOwl code also changes. You might see errors if the version is newer. We strive to make the code in the main branch compatible with the latest Rust version, which is specified in the channel file.

Note that by using normal cargo command RustOwl will be built with nightly compiler since there is a rust-toolchain.toml which specifies nightly compiler for development environment.

VS Code extension

For VS Code extension, we use pnpm to setup environment.
To get started, you have to install dependencies by running following command inside vscode directory:

bash
pnpm install

Neovim Plugin

You need to install stylua and selene for code formatting and linting.

Now write your code, see lua, ftplugin, nvim-tests.

Please write a test using mini.test before submitting a pr, you can run tests using run_nvim_tests.sh.

Emacs Plugin

You need to install eask for code formatting, linting, testing and whatnot.

Now write your code, see rustowl.el and emacs-tests.

Please write a test using ERT before submitting a pr, you can run tests using eask script run test. (needs eask)

Before submitting PR

Before submitting PR, you have to check below:

Development Checks

Use the helper scripts in ./scripts to run development checks and fixes. Examples:

bash
# Run all development checks
./scripts/dev-checks.sh

# Run checks and automatically fix issues where possible
./scripts/dev-checks.sh --fix

Refer to individual scripts for details (security, benchmarking, size checks). For CI-consistent runs, prefer the scripts above instead of invoking tools manually.

Security and Memory Safety Testing

Use ./scripts/security.sh to run the available security and UB detection tools. See the script for options to skip Miri/Valgrind/audit steps.

Performance Testing

Use ./scripts/bench.sh to run performance benchmarks. Create and compare baselines using the script's flags.

Binary Size Monitoring

Use ./scripts/size-check.sh to analyze and compare binary sizes. Save/Load baselines via the script flags.

Manual Checks

If the automated scripts are not available, ensure:

Rust code correctness and formatting

  • Correctly formatted by cargo fmt
  • Linted using Clippy by cargo clippy
  • All tests pass with cargo test

VS Code extension Style

  • Correctly formatted by pnpm fmt
  • Linting passes with pnpm lint
  • Type checking passes with pnpm check-types

Neovim Plugin Checks

  • Correctly formatted by stylua
  • Linting passes with selene
  • Tests passing with mini.test

Development Workflow

  1. Before making changes:

    bash
    # Create performance baseline
    ./scripts/bench.sh --save before-changes
  2. During development:

    bash
    # Run quick checks frequently
    ./scripts/dev-checks.sh --fix
  3. Before committing:

    bash
    # For Rust Code
    ./scripts/dev-checks.sh
    ./scripts/security.sh
    ./scripts/bench.sh --load before-changes
    ./scripts/size-check.sh
    # For Neovim
    ./scripts/run_nvim_tests.sh
    stylua .
    selene .
    # For Emacs
    eask script run test
    eask script run format
    eask lint <linter>

Troubleshooting

Script Permissions

bash
chmod +x scripts/*.sh

Missing Tools

Install the required tools manually using the platform-specific commands in the Prerequisites section above.

CI Failures

  • Check workflow logs for specific error messages
  • Verify rust-toolchain.toml compatibility
  • Ensure scripts have execution permissions
  • Test locally with the same script used in CI

For more detailed information about the scripts, see scripts/README.md.


SECURITY (`docs/SECURITY.md`)

Security Reporting

If you wish to report a security vulnerability but not in a public issue -- thank you! -- we ask that you follow the following process.

Please report security vulnerabilities by filling out the following template:

  • PUBLIC: Please let us know if this vulnerability has been made or discussed publicly already, and if so, please let us know where.
  • DESCRIPTION: Please provide precise description of the security vulnerability you have found with as much information as you are able and willing to provide.

Please send the above info, along with any other information you feel is pertinent to: [email protected].

In addition, you may request that the project provide you a patched release in advance of the release announcement, however, we can not guarantee that such information will be provided to you in advance of the public release and announcement.

However, I will email you at the same time the public announcement is made.
We will let you know within a few weeks whether or not your report has been accepted or rejected.
We ask that you please keep the report confidential until we have made a public announcement.


Build (`docs/build.md`)

Build guide

Here we describe how to build each of our repository.

RustOwl

You can choose one of two ways to build RustOwl.

You can add runtime directory paths to the search paths by specifying the RUSTOWL_RUNTIME_DIRS or RUSTOWL_SYSROOTS environment variables.
The default runtime directory is $HOME/.rustowl.

On a freshly installed Ubuntu system, you need to run apt install build-essential to ensure all required build tools are available for linking.

Build RustOwl using stable toolchain

There are scripts to build the stable version of RustOwl.
scripts/toolchain sets up the RustOwl toolchain and executes command using that toolchain.

bash
./scripts/toolchain cargo install --path . --locked

Build RustOwl using custom toolchain

You can build RustOwl using a custom toolchain. This method is recommended for contributors to RustOwl.

The prerequisites are as follows:

  • rustup installed
    • You can install rustup from this link.
    • You need to set up the PATH environment variable. Follow the instructions provided by the rustup installer to do this.
  • gcc or clang installed.
    • On Windows, you can install the Visual Studio toolchain instead.

Building RustOwl requires a nightly build of rustc. It will be installed automatically by rustup if needed.

Other dependencies are specified in the configuration files and will be installed automatically.

bash
cargo install --path . --locked

VSCode extension

Prerequisite

  • VS Code installed
  • Node.js installed
  • pnpm installed
    • After installing Node.js, You can install pnpm by running npm install pnpm.

VS Code extension has been tested on macOS Sequoia 15.3.2 on arm64 architecture with Visual Studio Code 1.99.3, Node.js v20.16.0, and pnpm 1.22.22.
Other dependencies are locked in the configuration files and will be installed automatically.

Build & Run

First, install the dependencies:

bash
cd vscode
pnpm install --frozen-lockfile

Then, open the vscode directory in VS Code.

A notification to install the recommended VS Code extension will appear in the bottom right corner of VS Code.
Click the install button, wait for the installation to finish, and then restart VS Code.

Open the vscode directory again, and press the F5 key in the VS Code window.
A new VS Code window with the extension enabled will appear.

Open the cargo workspace directory in the new VS Code window.

When you save Rust files, decorations indicating the movement of ownership and lifetimes will appear in the editor.


Emacs Configuration (`docs/emacs-configuration.md`)

RustOwl Configuration (Emacs)

Table Of Contents

Quick Start

  1. Install RustOwl (see docs/installation.md) and install the Emacs package via elpaca or use-package.
  2. Open a Rust Cargo workspace in Emacs.
  3. Place the cursor on a variable and wait ~2 seconds (default) to see overlays with lifetime and borrow info.

This document describes how to use RustOwl from Emacs. It mirrors the detail level of docs/neovim-configuration.md and is derived from rustowl.el.

Basic Setup

Install the rustowl executable (see docs/installation.md). Install the Emacs package via elpaca or use-package.

Elpaca example:

elisp
(elpaca
  (rustowl
    :host github
    :repo "cordx56/rustowl"))

Use-package example:

elisp
(use-package rustowl
  :after lsp-mode)

What the package does

  • Registers an LSP client for rust-mode, rust-ts-mode, and rustic-mode using rustowl as the executable.
  • Sends rustowl/analyze on save (see rustowl-enable-analyze-on-save).
  • Sends rustowl/cursor when the cursor has been idle for rustowl-cursor-timeout seconds (default 2.0 seconds) and applies underlines via overlays.

Defaults and variables

  • rustowl-cursor-timeout (float) โ€” default: 2.0 seconds. Idle time before the cursor request is sent.
  • rustowl-cursor-timer โ€” internal timer variable managed by the package.

How highlighting works

  • The package sends rustowl/cursor with position and document URI. The server responds with decorations (type, range, hover text, overlapped).
  • For each non-overlapped decoration, the package maps types to underline colors:
    • definitely_live โ†’ #00cc00 (green, straight)
    • maybe_initialized โ†’ #00cc00 (green, wavy)
    • imm_borrow โ†’ #0000cc (blue)
    • mut_borrow โ†’ #cc00cc (purple)
    • move / call โ†’ #cccc00 (yellow)
    • outlive / shared_mut โ†’ #cc0000 (red, wavy)
  • Underlines are implemented via overlays with face (:underline (:color <color> :style wave)).

Enabling / Disabling

  • rustowl-enable-cursor โ€” enable cursor-based highlighting for current buffer (adds post-command-hook).
  • rustowl-disable-cursor โ€” disable cursor-based highlighting (removes hook, cancels timer and clears overlays).
  • Cursor highlighting is automatically enabled for Rust buffers via rust-mode-hook, rust-ts-mode-hook, and rustic-mode-hook.
  • Obsolete aliases enable-rustowl-cursor / disable-rustowl-cursor remain for backward compatibility.

Examples

To disable analyze-on-save globally, remove the hooks or call:

elisp
(remove-hook 'rust-mode-hook #'rustowl-enable-cursor)

To customize the timeout in your config:

elisp
(setq rustowl-cursor-timeout 1.0) ; 1 second

Troubleshooting

  • If overlays don't appear, ensure lsp-mode is active and rustowl server is reachable.
  • Use M-x rustowl-clear-overlays to clear overlays if they persist.

See docs/lsp-spec.md for the LSP request/response shapes.


Installation (`docs/installation.md`)

Installation

Table Of Contents

Quick Start

  • Install using our script (recommended):
text
curl -L https://raw.githubusercontent.com/cordx56/rustowl/refs/heads/main/scripts/installer | sh

cargo-binstall

Install the prebuilt binary using cargo-binstall:

bash
cargo binstall rustowl

This automatically downloads and unpacks a Rust toolchain if required.

Windows (winget)

Install with:

sh
winget install rustowl

Arch Linux (AUR)

We provide AUR packages that either install prebuilt binaries or build from source.
Prebuilt binaries (recommended):

sh
yay -S rustowl-bin

Build from AUR (cargo build):

sh
yay -S rustowl

Git (build from latest source):

sh
yay -S rustowl-git

Replace yay with your AUR helper of choice.

Nix flake

There is a third-party Nix flake repository in the Nix community.

GitHub Releases

Download the rustowl executable from the release page:

https://github.com/cordx56/rustowl/releases/latest

Place the executable into a directory on your PATH.

Docker

Run the prebuilt image from GitHub Container Registry:

sh
docker pull ghcr.io/cordx56/rustowl:latest

Run it against a project directory:

sh
docker run --rm -v /path/to/project:/app ghcr.io/cordx56/rustowl:latest

Use an alias to act like a local CLI:

sh
alias rustowl='docker run --rm -v $(pwd):/app ghcr.io/cordx56/rustowl:latest'

Build manually

See docs/build.md for detailed build instructions and how to build editor extensions.


Lsp Spec (`docs/lsp-spec.md`)

The RustOwl LSP specification

Table of Contents

rustowl, is an LSP server which provides RustOwl information.
To display various types of decorations, RustOwl supports some custom methods from the client.

Here, we describe the specifications of those custom methods.

Types

Here, we describe the types we will use in this document.

`OprType`

typescript
"lifetime" | "definitely_live" | "maybe_initialized"
| "imm_borrow" | "mut_borrow" | "move" | "call"
| "outlive" | "shared_mut"

definitely_live and maybe_initialized are derived from a CFG-based liveness analysis:

  • definitely_live covers ranges where the local is provably initialized on every path that reaches the location (state is exactly {Initialized}).
  • maybe_initialized covers ranges where the local is initialized on at least one path but may also have been moved, dropped, or be uninitialized on others (state contains Initialized together with other variants). Useful for spotting conditional drops and other resource-management ambiguity.

lifetime is retained for backward compatibility; clients should treat definitely_live as the replacement for the previous "actual lifetime" decoration.

`Decoration`

{
"type": OprType,
"range": Range,
"hover_text": Option<String>,
"overlapped": bool
}

overlapped field indicates that the decoration is overlapped and should be hidden.

Methods

We describe the custom methods used in RustOwl.

`rustowl/cursor`

Request payload

{
"position": Position,
"document": {
"uri": DocumentUri
}
}

Response payload

{
"is_analyzed": bool,
"decorations": [Decoration]
}


Neovim Configuration (`docs/neovim-configuration.md`)

RustOwl Configuration (Neovim)

Quick Start

  1. Install RustOwl (see docs/installation.md) and the Neovim plugin via your plugin manager.
  2. Add the minimal setup to your lazy.nvim config and open a Rust Cargo workspace.
  3. Hover the cursor over a variable and wait ~500ms to see visualizations.

This document describes all available configuration options for the RustOwl Neovim plugin.

Table of Contents

Basic Setup

The minimal configuration for RustOwl using lazy.nvim:

lua
{
  'cordx56/rustowl',
  version = '*', -- Latest stable version
  build = 'cargo install rustowl',
  lazy = false, -- This plugin is already lazy
  opts = {},
}

Configuration Options

All configuration options are optional and have sensible defaults:

`auto_attach` (boolean)

  • Default: true
  • Description: Automatically attach the RustOwl LSP client when opening a Rust file.

`auto_enable` (boolean)

  • Default: false
  • Description: Enable RustOwl highlighting immediately when the LSP client attaches. When false, you need to manually enable highlighting using :Rustowl enable or require('rustowl').enable().

`idle_time` (number)

  • Default: 500
  • Description: Time in milliseconds to hover with the cursor before triggering RustOwl analysis and highlighting.

`highlight_style` (table)

  • Description: The style of underline for different highlight types. 'undercurl' or 'underline'.

`colors` (table)

  • Description: Custom colors for different highlight types. Each color should be a hex color string (e.g., '#ff0000').

Available Color Options:

  • lifetime: Color for variable lifetime highlights (default: '#00cc00' - green)
  • imm_borrow: Color for immutable borrow highlights (default: '#0000cc' - blue)
  • mut_borrow: Color for mutable borrow highlights (default: '#cc00cc' - purple)
  • move: Color for value move highlights (default: '#cccc00' - yellow)
  • call: Color for function call highlights (default: '#cccc00' - yellow)
  • outlive: Color for lifetime error highlights (default: '#cc0000' - red)

`client` (table)

  • Description: LSP client configuration that gets passed to vim.lsp.start. This follows the same structure as Neovim's LSP client configuration.

Customizing Highlight Colors

Default Highlight Style

Default highlight style is straight underline except for maybe live and errors.

Default Colors

The default color scheme uses the following colors that correspond to the visual legend:

  • ๐ŸŸฉ Green (#00cc00): Variable's lifetime
  • ๐ŸŸฆ Blue (#0000cc): Immutable borrowing
  • ๐ŸŸช Purple (#cc00cc): Mutable borrowing
  • ๐ŸŸง Yellow (#cccc00): Value moved / function call
  • ๐ŸŸฅ Red (#cc0000): Lifetime errors

Custom Colors

To customize colors, specify them in the colors table:

lua
opts = {
  colors = {
    lifetime = '#32cd32',   -- Lime green
    imm_borrow = '#4169e1', -- Royal blue
    mut_borrow = '#ff69b4', -- Hot pink
    move = '#ffa500',       -- Orange
    call = '#ffd700',       -- Gold
    outlive = '#dc143c',    -- Crimson
  },
}

Color Format

Colors must be specified as hex color strings:

  • โœ… Valid: '#ff0000', '#00ff00', '#0000ff'
  • โŒ Invalid: 'red', 'rgb(255,0,0)', '#f00'

Partial Color Customization

You can customize only specific colors while keeping the defaults for others:

lua
opts = {
  colors = {
    lifetime = '#90ee90',   -- Light green for better visibility
    outlive = '#ff4500',    -- Orange red for errors
    -- Other colors will use defaults
  },
}

Examples

Example 1: Minimal Configuration

lua
{
  'cordx56/rustowl',
  version = '*',
  build = 'cargo install rustowl',
  lazy = false,
  opts = {},
}

Example 2: Auto-enable with Custom Colors

lua
{
  'cordx56/rustowl',
  version = '*',
  build = 'cargo install rustowl',
  lazy = false,
  opts = {
    auto_enable = true,
    colors = {
      lifetime = '#90ee90',   -- Light green
      imm_borrow = '#87ceeb', -- Sky blue
      mut_borrow = '#dda0dd', -- Plum
      move = '#f0e68c',       -- Khaki
      call = '#ffd700',       -- Gold
      outlive = '#ff6347',    -- Tomato
    },
  },
}

Example 3: Custom Key Binding and Styling

lua
{
  'cordx56/rustowl',
  version = '*',
  build = 'cargo install rustowl',
  lazy = false,
  opts = {
    auto_enable = false,
    idle_time = 300,
    highlight_style = {
      definitely_live = 'underline',
      maybe_initialized = 'undercurl',
    },
    colors = {
      outlive = '#ff0000', -- Bright red for errors
    },
    client = {
      on_attach = function(_, buffer)
        vim.keymap.set('n', '<leader>ro', function()
          require('rustowl').toggle(buffer)
        end, { buffer = buffer, desc = 'Toggle RustOwl' })

        vim.keymap.set('n', '<leader>re', function()
          require('rustowl').enable(buffer)
        end, { buffer = buffer, desc = 'Enable RustOwl' })

        vim.keymap.set('n', '<leader>rd', function()
          require('rustowl').disable(buffer)
        end, { buffer = buffer, desc = 'Disable RustOwl' })
      end
    },
  },
}

Example 4: Dark Theme Optimized Colors

lua
{
  'cordx56/rustowl',
  version = '*',
  build = 'cargo install rustowl',
  lazy = false,
  opts = {
    colors = {
      lifetime = '#50fa7b',   -- Dracula green
      imm_borrow = '#8be9fd', -- Dracula cyan
      mut_borrow = '#ff79c6', -- Dracula pink
      move = '#f1fa8c',       -- Dracula yellow
      call = '#ffb86c',       -- Dracula orange
      outlive = '#ff5555',    -- Dracula red
    },
  },
}

Example 5: For init.vim Users

If you're using init.vim instead of init.lua, you can configure RustOwl using Vim script:

vim
lua << EOF
require('lazy').setup({
  {
    'cordx56/rustowl',
    version = '*',
    build = 'cargo install rustowl',
    lazy = false,
    opts = {
      colors = {
        lifetime = '#00ff00',
        imm_borrow = '#0080ff',
        mut_borrow = '#ff00ff',
        move = '#ffff00',
        call = '#ffa500',
        outlive = '#ff0000',
      },
    },
  },
})
EOF

Usage Commands

When opening a Rust file, the following commands become available:

  • :Rustowl start_client - Start the RustOwl LSP client
  • :Rustowl stop_client - Stop the RustOwl LSP client
  • :Rustowl restart_client - Restart the RustOwl LSP client
  • :Rustowl enable - Enable RustOwl highlighting
  • :Rustowl disable - Disable RustOwl highlighting
  • :Rustowl toggle - Toggle RustOwl highlighting

You can also use the Lua API:

lua
require('rustowl').enable()   -- Enable highlighting
require('rustowl').disable()  -- Disable highlighting
require('rustowl').toggle()   -- Toggle highlighting
require('rustowl').is_enabled() -- Check if enabled

Usage (`docs/usage.md`)

RustOwl usage

Table of Contents

Quick Start

  1. Install RustOwl (docs/installation.md) and the editor extension for your editor.
  2. Open a Rust Cargo workspace and save a Rust file to trigger analysis.
  3. Place the cursor on a variable or function call and wait a short idle time to see visualizations.

Here, I describe how to use RustOwl in your workflow.

Getting started

First, please install the RustOwl LSP server and extension for your editor.
Installation guide is on installation file.

Then, please open a Rust source code file (.rs) in the editor.
RustOwl only works with a Cargo workspace, so you need to open the source code that is part of a Cargo workspace.
I recommend you try RustOwl with a small, simple workspace first.
RustOwl's analysis may take a long time for a large workspace.

VS Code extension will automatically start analyzing the workspace.
For other editors, you may need to enable RustOwl manually, but you can enable automatic loading in your configuration file.
The progress of analysis will be shown in your editor.

After the analysis started, RustOwl waits for your request.
Please place the text cursor on a variable or function call you would like to inspect.

RustOwl works for the analyzed portion, even if the entire analysis has not finished.
If your program has some fatal errors (e.g., syntax errors or unrecoverable type errors), RustOwl cannot work for the part where the analysis failed.

Wait for a few seconds, and then the ownership-related operations and lifetimes of the variable to which the unwrap() method call assigns a value will appear.

Basic usage

Basically, RustOwl can be used to resolve ownership and lifetime errors.
What RustOwl visualizes is:

  • Lifetime of variables, split into two precisions:
    • Definitely live range: the variable is provably initialized on every path that reaches the location.
    • Maybe live range: the variable is initialized on at least one path but may have been moved, dropped, or be uninitialized on others.
  • Shared (immutable) borrowing of variables
  • Mutable borrowing of variables
  • Value movement
  • Function call
  • Ownership and lifetime errors

You can see which color is assigned to them on the top page of this repository.
RustOwl can be used to see where a variable lives, where it dies, and where it is borrowed or moved.

For VS Code, you can see the message that explains the meaning of the underline by hovering your mouse cursor over it.

This is the basic usage of RustOwl!
Now you have a master's degree in RustOwl.

Advanced usage

The lifetime that RustOwl visualizes is the range of these variables:

  • Where the variable lives
    • For the meaning of NLL
  • Until the variable is dropped or moved
    • For the meaning of RAII

Based on this, we can use RustOwl as listed below:

  • To resolve dead lock that is caused by some data structures like Mutex
    • Because these locks are freed where the lock object is dropped
  • To manage resources
    • Like memories, files, and anything which is managed by RAII respective

Definitely live vs. maybe live

The lifetime visualization is split by a CFG-based liveness analysis into two precisions, which is useful when ownership depends on control flow.

rust
fn f(cond: bool) {
    let v: Vec<_> = (0..100).collect();
    if cond {
        drop(v); // v is dropped only on the `cond == true` branch
    }
    // After the `if`, v is "maybe live": still owned on the `false`
    // branch, but already dropped on the `true` branch.
}
  • A definitely live range marks code where the variable is owned on every path. Dropping or freeing a resource here is safe.
  • A maybe live range marks code where the variable is owned on some paths only. Reading from or moving the variable here would not compile, and it is a hint that resource cleanup is happening conditionally.

When auditing resource management (file handles, locks, allocations), the boundary between the two ranges is typically the location to inspect.

Did you get a Ph.D. in lifetimes?
So let's try managing resources with RustOwl.
You will get a Ph.D. in RustOwl and computer resource management.


Vscode Configuration (`docs/vscode-configuration.md`)

RustOwl Configuration (VS Code)

Table Of Contents

This document describes configuration options for the RustOwl VS Code extension.

Quick Start

Install the extension from the Marketplace or Open VSX. The extension will download and run the rustowl binary automatically when activated.

  1. Install the extension in VS Code.
  2. Open a Rust Cargo workspace.
  3. Save a Rust file and hover over a variable after a short idle time to see decorations.

Configuration

The VS Code extension exposes configurable settings and commands. Here are the concrete settings and behavior derived from code.

Settings (configuration keys)

  • rustowl.underlineThickness (string) โ€” The stroke thickness of the underline line. Allowed values: "1", "2", "3", "4".
  • rustowl.lifetimeColor (string) โ€” The color of the lifetime (definitely live).
  • rustowl.maybeInitColor (string) โ€” The color of the lifetime (maybe initialized).
  • rustowl.moveCallColor (string) โ€” The color of the move/call.
  • rustowl.immutableBorrowColor (string) โ€” The color of the immutable borrow.
  • rustowl.mutableBorrowColor (string) โ€” The color of the mutable borrow.
  • rustowl.outliveColor (string) โ€” The color of the outlive.
  • rustowl.displayDelay (number) โ€” Delay in displaying underlines (ms).
  • rustowl.highlightBackground (boolean) โ€” Highlight text background instead of underline.
  • rustowl.defaultEnabled (boolean) โ€” Enabled by default.

Commands

  • rustowl.hover โ€” manually request hover/decorations for current selection.
  • rustowl.toggle โ€” toggle decorations on/off (status bar changes accordingly).

Behavior

  • The extension bootstraps the rustowl binary using bootstrap.ts and starts an LSP client pointing at the executable.
  • On save of Rust files the extension sends rustowl/analyze to the server (if enabled).
  • On cursor selection changes, after displayDelay ms the extension sends rustowl/cursor and applies decorations returned by server.
  • Decorations ignore overlapped ranges and map LSP response types to decorations (lifetime, imm_borrow, mut_borrow, move/call, outlive/shared_mut).

Examples

Add settings to .vscode/settings.json:

json
{
  "rustowl.displayDelay": 2000,
  "rustowl.defaultEnabled": true,
  "rustowl.lifetimeColor": "#00cc00",
  "rustowl.maybeInitColor": "#00cc00",
  "rustowl.immutableBorrowColor": "#0000cc",
  "rustowl.mutableBorrowColor": "#cc00cc",
  "rustowl.moveCallColor": "#cccc00",
  "rustowl.outliveColor": "#cc0000",
  "rustowl.highlightBackground": false,
  "rustowl.underlineThickness": 2
}

Troubleshooting

  • Check Output -> RustOwl for logs. If the server failed to start, the extension shows an error message.
  • Ensure the rustowl executable is in PATH or let the extension download it (bootstrap).

--- METRICS ---