# rust-lang/rust-mode: the small Emacs major mode that stays out of the way

> rust-mode is the Rust major mode for Emacs, providing syntax highlighting, indentation and integration with Cargo, rustfmt and clippy, and deliberately not providing completion or jump-to-definition, which are left to eglot, lsp-mode, tree-sitter or rustic. It is in the Rust organisation, dual licensed, and its last tagged release is from 2022 even though the repository is still being worked on.

**rust-lang/rust-mode** — Emacs configuration for Rust

- Repository: https://github.com/rust-lang/rust-mode
- Stars: 1,277 · Forks: 197
- Language: Emacs Lisp
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/rust-lang-rust-mode

## What it does, and what it refuses to do

rust-mode makes editing Rust code with Emacs enjoyable. It requires Emacs 25 or later, and it is included in both Emacs Prelude and Spacemacs by default, which is the fastest way to judge it, since those two distributions ship it without configuration.

The feature list is short and the omissions are the point. It provides syntax highlighting, for Font Lock Mode, indentation, and integration with Cargo, clippy and rustfmt. And it states plainly that it does not provide auto completion, or jumping to function and trait definitions, with a pointer to the auto completion section for how to enable those.

The reason for the omissions is stated too, and it is a design position rather than a shortage of effort. If you are missing features in rust-mode, the README asks you to check out rustic before opening a feature request, because rustic depends on rust-mode and provides additional features, and this allows the project to keep rust-mode light-weight for users who are happy with basic functionality.

So the division of labour is: rust-mode is the editing substrate, everything else is a layer you opt into. That is why the package is still useful years after it stopped releasing tagged versions, and it is the first thing to understand before you configure it.

There is one known issue, and it is a performance one rather than a correctness one. `rust-syntax-propertize` and `adaptive-wrap-prefix-mode` can lead to severe lag when editing larger files.

## Three installation routes

The package is on MELPA and on NonGNU ELPA, and it can also be cloned, and the README covers all three.

For MELPA you add the archive to your package list and refresh:

``` elisp
(require 'package)
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(package-refresh-contents)
```

then install with `M-x package-install rust-mode` and load it with `(require 'rust-mode)` in your configuration.

NonGNU ELPA can be used out of the box in emacs28, and for older versions you add the archive yourself:

``` elisp
(with-eval-after-load 'package (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/")))
```

The manual route is to clone the repository locally and point Emacs at it:

``` elisp
(add-to-list 'load-path "/path/to/rust-mode/")
(autoload 'rust-mode "rust-mode" nil t)
(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode))
```

The manual route is also the one that makes the repository layout visible, since a checkout shows the mode split into its concerns, with `rust-mode.el` as the main mode, `rust-common.el`, `rust-cargo.el`, `rust-compile.el`, `rust-rustfmt.el`, `rust-prog-mode.el`, `rust-playpen.el` and `rust-utils.el`, plus `rust-mode-treesitter.el` for the tree-sitter integration and the test files `rust-mode-tests.el` and `rust-cargo-tests.el`.

## Cargo runs through the compile machinery

The run, test, build and check functions are `rust-run`, `rust-test`, `rust-compile` and `rust-check`, and they shell out to Cargo. Under the hood they use the standard Emacs `compile` function, which is the detail that makes them feel native rather than bolted on, since the output lands in the same compile buffer as everything else in your session and inherits the same key bindings for moving between errors.

The default bindings are unusual, and the README lists them so you can move them:

- `C-c C-c C-u` for `rust-compile`
- `C-c C-c C-k` for `rust-check`
- `C-c C-c C-t` for `rust-test`
- `C-c C-c C-r` for `rust-run`

The extra `C-c` prefix is there so these do not collide with the plain `C-c C-c` compile command, and there is one refinement worth knowing. To run programs that require user input you use the universal argument when invoking `rust-run`, which is `C-u C-c C-c C-r`, and that is how you get an interactive program to keep its terminal rather than having its output swallowed by the compile buffer.

Clippy has its own entry point. `rust-run-clippy` runs Clippy, the linter, and is bound to `C-c C-c C-l` by default.

There is also a small quality of life command that is oddly popular among people who write Rust. `rust-dbg-wrap-or-unwrap` either wraps or unwraps the current region in `dbg!`, which is the quickest way to put a print statement around an expression, and it is bound to `C-c C-d`.

## rustfmt, spaces instead of tabs, and prettifying symbols

Three small things make an editor feel like it was written for the language.

First, indentation. Commands like TAB should indent correctly, and since the Rust style guide recommends spaces rather than tabs, the README suggests forcing it:

``` elisp
(add-hook 'rust-mode-hook
          (lambda () (setq indent-tabs-mode nil)))
```

Since Emacs 24.4, `electric-indent-mode` is turned on by default, and if you dislike that you call `(electric-indent-mode 0)` in `rust-mode-hook`.

Second, formatting. The `rust-format-buffer` function formats your code with rustfmt if it is installed, and by default it is bound to `C-c C-f`. Formatting on save is a variable rather than a hook, `rust-format-on-save`, so you enable it with `(setq rust-format-on-save t)`.

Third, prettifying. `M-x prettify-symbols-mode` turns `x.add(y)` into something more mathematical, and to have it on for all Rust files you add the hook. You can extend the substitutions yourself through `rust-prettify-symbols-alist`, and the README's example is showing `x.add(y)` as `x∔(y)`, which is the kind of detail that tells you the package has a user who cares about how the code reads.

The mode also has `rust-toggle-mutability`, which toggles `mut` for the variable defined at the current line, listed in the README as one of the additional commands.

## tree-sitter and LSP are opt-in layers

Everything that makes rust-mode a modern Rust editor is off by default, and the README shows how to turn each piece on.

For tree-sitter there is a native mode, `rust-ts-mode`, enabled with a single setting:

``` elisp
(use-package rust-mode
  :init
  (setq rust-mode-treesitter-derive t))
```

If you want tree-sitter but cannot use Emacs 29.1, the README points at the elisp-tree-sitter project, and with the dependencies installed the feature is activated with `tree-sitter-langs`, `global-tree-sitter-mode` and a `tree-sitter-after-on-hook` that enables `tree-sitter-hl-mode`.

For the language server there are two choices, and the difference in weight is the difference between the two packages. eglot is described as a lightweight LSP client, and it is one line:

``` elisp
(add-hook 'rust-mode-hook 'eglot-ensure)
```

lsp-mode provides more features and can be enhanced with additional packages, with its own wiki page for the vanilla Emacs installation.

The other recommended packages section names flycheck for diagnostics, `cargo.el`, `cargo-mode` and rustic, and the last of those is the one the README has already pointed you at earlier, since rustic is where the heavier functionality lives.

## Where rust-mode is the wrong choice

The first wrong case is a team that wants one tool. rust-mode is the substrate and everything else is a separate package, so a new contributor has to assemble eglot or lsp-mode, tree-sitter, flycheck and possibly rustic before the editor feels complete. That assembly is usually a config file somebody else has already written, but it is still work, and the README's own advice to use rustic instead of filing feature requests is a sign of where the real experience lives.

The second wrong case is an editor that is not Emacs. This is an Emacs major mode, and the alternatives for Rust have their own ecosystems, so a team standardised on another editor gets nothing from this package beyond the conventions it encodes.

The third is a machine on an old Emacs. The floor is Emacs 25, and the tree-sitter path wants 29.1 for the native mode, so the gap between the minimum and the pleasant configuration is wide enough that the mode is fine but the experience is not.

The fourth is large files. The known issue says plainly that `rust-syntax-propertize` and `adaptive-wrap-prefix-mode` can lead to severe lag when editing larger files, and neither is on by default, so the fix is to leave them off rather than to abandon the mode.

On the state of the project, the last tagged releases were 1.0.3 on 2021-12-18, 1.0.4 on 2022-02-17 and 1.0.5 on 2022-06-26, while the last push to the repository was on 2026-10-02, and the repository is not archived. So the code is still being worked on without new tags, and the release line you are likely to get from MELPA is a 2022 build. The tests run through Eask, with the `test` target running the ERT suites in `rust-mode-tests.el` and `rust-cargo-tests.el`, there is a `checkdoc` target for the documentation lint and a `lint` target for the package, a `Dockerfile` style script for running the tests in a container, and a `triagebot.toml` for the bot that labels incoming issues. The tree carries both an Apache licence file and an MIT licence file.

## Conclusion

Adopt rust-mode if you edit Rust in Emacs and want a dependable core that is deliberately small, because the package keeps its feature list to highlighting, indentation and the Cargo toolchain, and everything heavier is an opt-in on top. Do not adopt it as a complete Rust editing environment, since completion and definition jumping are explicitly not part of it and you will have to add eglot, lsp-mode, tree-sitter or rustic yourself. Verify first that your Emacs version is 25 or later, that you know which of the two completion stacks you are wiring up because the README documents both, and that your key bindings do not collide with the compile style ones, since the C-c C-c prefixed bindings are unusual and easy to press by accident.

## FAQ

### What does rust-mode provide for Emacs?

Syntax highlighting, indentation, and integration with Cargo, clippy and rustfmt. It requires Emacs 25 or later, ships in Emacs Prelude and Spacemacs by default, and explicitly does not provide auto completion or jumping to function and trait definitions.

### How do I install rust-mode from MELPA?

Add the MELPA archive to `package-archives` in your init file, call `package-initialize` and `package-refresh-contents`, then install with `M-x package-install rust-mode` and load it with `(require 'rust-mode)`. NonGNU ELPA works out of the box in Emacs 28, and a manual installation just needs the load path, an autoload and an entry in `auto-mode-alist`.

### Which key bindings does rust-mode use for Cargo commands?

They are bound through the standard Emacs compile function, with `C-c C-c C-u` for rust-compile, `C-c C-c C-k` for rust-check, `C-c C-c C-t` for rust-test and `C-c C-c C-r` for rust-run, and `C-c C-c C-l` for rust-run-clippy. To run a program that needs input you use the universal argument, `C-u C-c C-c C-r`.

### Does rust-mode include completion or jump to definition?

No, and the README is explicit that it does not provide auto completion or jumping to function and trait definitions. You add them yourself, either with the lightweight eglot client through `(add-hook 'rust-mode-hook 'eglot-ensure)` or with lsp-mode, and the tree-sitter highlighting is enabled with `(setq rust-mode-treesitter-derive t)`.

### What are the known performance issues with rust-mode?

The README lists one: `rust-syntax-propertize` and `adaptive-wrap-prefix-mode` can lead to severe lag when editing larger files. Neither is enabled by default, and the project recommends checking rustic for anything beyond the basic functionality before opening a feature request.

## Sources

- [Issues](https://github.com/rust-lang/rust-mode/issues)
- [License: Apache-2.0](https://github.com/rust-lang/rust-mode/blob/main/LICENSE)
- [README](https://github.com/rust-lang/rust-mode/blob/main/README.md)
- [Releases](https://github.com/rust-lang/rust-mode/releases)
- [rust-lang/rust-mode on GitHub](https://github.com/rust-lang/rust-mode)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rust-lang-rust-mode
