Rust-Rust mode: rust-mode — Rust Major Mode


Table of Contents


rust-mode makes editing Rust code with Emacs enjoyable. It requires Emacs 25 or later, and is included in both Emacs Prelude and Spacemacs by default.

This mode provides:

  • Syntax highlighting (for Font Lock Mode)
  • Indentation
  • Integration with Cargo, clippy and rustfmt

This mode does not provide autocompletion, or jumping to function / trait definitions. See Integration with Rust Language Server below for tips on how to enable this.



The package is available on MELPA. Add this to your init.el.

(require 'package)
(add-to-list 'package-archives
             '("melpa" . "") t)

Now you can install rust-mode with:

M-x package-install rust-mode

And put this in your config to load rust-mode automatically:

(require 'rust-mode)

Manual installation

Clone this repository locally, and add this to your init.el:

(add-to-list 'load-path "/path/to/rust-mode/")
(autoload 'rust-mode "rust-mode" nil t)

Feature guide


Commands like TAB should indent correctly.

The Rust style guide recommends spaces rather than tabs for indentation; to follow the recommendation add this to your init.el, which forces indentation to always use spaces.

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

Code formatting

The rust-format-buffer function will format your code with rustfmt if installed. By default, this is bound to C-c C-f.

The variable rust-format-on-save enables automatic formatting on save. For example, add the following in your init.el to enable format on save:

(setq rust-format-on-save t)

Running / testing / compiling code

The rust-run, rust-test and rust-compile functions shell out to Cargo to run, test or build your code. Under the hood, these use the standard Emacs compile function.

These are not bound by default. To bind these to keyboard shortcuts, you can use the following in your init.el:

(define-key rust-mode-map (kbd "C-c C-c") 'rust-run)


rust-run-clippy runs Clippy, a linter.

Easy insertion of !dbg

rust-dbg-wrap-or-unwrap either wraps or unwraps the current region in dbg!. This can be useful for easily adding debug lines to your program.

This is bound to C-c C-d by default.

Other recommended packages

Auto-completion / code navigation

This package does not provide integration with RLS, which provides auto-completion and code navigation. To use this you need an Emacs package that supports LSP.

Two examples are:

A lighter package that uses racer is emacs-racer.


flycheck allows highlighting compile errors and Clippy lints inline.


cargo.el provides a minor mode for integration with Cargo, Rust's package manager.


rustic is a fork of rust-mode, extending it with other features such as integration with LSP and with flycheck.

For package maintainers


The file rust-mode-tests.el contains tests that can be run via ERT. You can use to run them in batch mode, if you set the environment variable EMACS to a program that runs emacs.


  • Improve documentation
    Improve documentation

    Feb 22, 2020

    What does exactly rust-mode do? It's unclear from reading the readme. A bullet list with features would be neat.

    documentation help wanted 
  • add documentation on how to use lsp-mode and eglot
    add documentation on how to use lsp-mode and eglot

    May 17, 2020

    We just had some questions from a rust-mode user on lsp-mode gitter.

  • Wrong indentation around trait bounds for output parameters.
    Wrong indentation around trait bounds for output parameters.

    May 21, 2020

    See #306.

    Minimal reproduction:

    pub fn f<Req, Resp>() -> MR<
        impl FnMut(ServiceResponse) -> Resp,
        Req: Send,
        Resp: Send,

    get reindented to

    pub fn f<Req, Resp>() -> MR<
        impl FnMut(ServiceResponse) -> Resp,
        Req: Send,
    Resp: Send,
  • Turbofish breaks method chaining
    Turbofish breaks method chaining

    May 25, 2020

    Turbofish syntax breaks method chaining alignment:

    let foo = bar.baz()
  • Method chaining broken inside expressions without newline
    Method chaining broken inside expressions without newline

    May 25, 2020

    Method chaining doesn't work at all inside expressions without a newline:



    This works:

  • Add the possibility to bind keys to functions for doc comments
    Add the possibility to bind keys to functions for doc comments

    May 29, 2020

    As of now using comment-line or comment-dwim will always open a comment with //.

    I needed some doc comments (/// or //!) so I implemented mine (inspired by, thanks a lot!) hoping it would help:

    (defun rust-doc-comment-dwim (c)
      "Comment or uncomment the current line or text selection."
      ;; If there's no text selection, comment or uncomment the line
      ;; depending whether the WHOLE line is a comment. If there is a text
      ;; selection, using the first line to determine whether to
      ;; comment/uncomment.
      (let (p1 p2)
        (if (use-region-p)
              (setq p1 (region-beginning) p2 (region-end))
              (goto-char p1)
              (if (wholeLineIsCmt-p c)
                  (my-uncomment-region p1 p2 c)
                (my-comment-region p1 p2 c)
            (if (wholeLineIsCmt-p c)
                (my-uncomment-current-line c)
              (my-comment-current-line c)
              )) )))
    (defun wholeLineIsCmt-p (c)
        (beginning-of-line 1)
        (looking-at (concat "[ \t]*//" c))
    (defun my-comment-current-line (c)
      (beginning-of-line 1)
      (insert (concat "//" c))
    (defun my-uncomment-current-line (c)
      "Remove “//c” (if any) in the beginning of current line."
      (when (wholeLineIsCmt-p c)
        (beginning-of-line 1)
        (search-forward (concat "//" c))
        (delete-backward-char 3)
    (defun my-comment-region (p1 p2 c)
      "Add “//c” to the beginning of each line of selected text."
      (interactive "r")
      (let ((deactivate-mark nil))
          (goto-char p2)
          (while (>= (point) p1)
            (my-comment-current-line c)
    (defun my-uncomment-region (p1 p2 c)
      "Remove “//c” (if any) in the beginning of each line of selected text."
      (interactive "r")
      (let ((deactivate-mark nil))
          (goto-char p2)
          (while (>= (point) p1)
            (my-uncomment-current-line c)
            (previous-line) )) ))
    (use-package rust-mode
      :mode "\\.rs'"
      :bind ("C-M-;" . rust-doc-comment-dwim-following)
      :bind ("C-M-," . rust-doc-comment-dwim-enclosing)
      (defun rust-doc-comment-dwim-following ()
        (rust-doc-comment-dwim "/"))
      (defun rust-doc-comment-dwim-enclosing ()
        (rust-doc-comment-dwim "!"))

    Which allows to comment as /// when pressing Ctrl-Alt-; and //! when pressing Ctrl-Alt-,

    It would actually be better to have these two functions available in the mode to bind them to some keys but I'm actually really bad at writing Lisp to do it

  • Melpa stable
    Melpa stable

    Jul 21, 2015

    could you please create a tag to use this package using melpa stable [1]. Thanks

    [1] :

  • Fix window scrolling after rustfmt is run
    Fix window scrolling after rustfmt is run

    May 6, 2018


    I tried investigating the window scrolling after rustfmt is run (this issue has been referenced in various forms in issues #268 #231 #251) as I am still experiencing it and imo it's pretty annoying.

    I'm not an elisp expert, but I think a quick fix/workaround could be to simply nail the window start position and restore it after rustfmt has finished.

    I think there may be still cases where the code reformatting can be a bit confusing (f.e. rustfmt'ing a big messed up file), but at least I'm not getting mad every time I compulsively save a buffer :-)

    Can you let me know what you think of this little patch? I tested it a bit and it has greatly improved my coding experience with rustfmt.

    Thanks for your time!

    EDIT: I did some checks, but I'm not sure how to fix those warning that make the CI fail EDIT2: maybe fixed (not sure if fix is correct though)

  • Significant slowdown with rust-match-angle-brackets non nil
    Significant slowdown with rust-match-angle-brackets non nil

    Apr 28, 2017

    I'm experiencing serious slowdowns when entering insert mode & navigating in general all of a sudden. Not sure what the cause is but figured I'd create an issue as it doesn't happen with rust-mode turned off.

    Here's the output from the profiler-report:

    Function                            Cpu samples   %
    timer-event-handler                       13844  92%
    command-execute                             782   5%
     ...                                        187   1%
    redisplay_internal (C function)             169   1%
    eldoc-pre-command-refresh-echo-area           5   0%
    sp--save-pre-command-state                    3   0%

    If I expand timer-event-handler, most of the CPU is being hogged on rust-in-macro within the up-list function. Also backward-up-list hogs a good chunk of CPU.

     - apply                                                         2171  77%
      - hl-paren-highlight                                           2090  74%
       - scan-sexps                                                  2077  74%
        - internal--syntax-propertize                                2077  74%
         - syntax-propertize                                         2077  74%
          - rust-syntax-propertize                                   2073  74%
           - rust-ordinary-lt-gt-p                                   2073  74%
            - rust-in-macro                                          1955  69%
             + rust-in-macro                                         1615  57%
             + backward-up-list                                       241   8%
             + rust-rewind-irrelevant                                  95   3%
             + rust-looking-back-ident                                  4   0%
            + rust-in-str-or-cmnt                                     118   4%

    Anyone experienced anything similar?

    Same issue on current nightly & stable (1.17) I've tried with a clean .spacemacs file and without any additional packages. It's only when rust-mode is enabled I run into this slowdown.

    The file I'm working on is less than 1k lines.

    Also as an aside, I've tried Emacs24, Ubuntu 15.04, 16.04 and now upgraded to 16.10. This is on a Virtual machine.

    Description:	Ubuntu 16.10
    Release:	16.10
    Codename:	yakkety
  • crippy bug: stdin buffer if
    crippy bug: stdin buffer if "Rust Format On Save" is on

    Mar 2, 2020

    It is random, I have no idea how to reproduce it.

    Time to time when I save current buffer with rust-mode, let's call it, emacs does not save it, and instead it open buffer stdin, in other words I press C-x s and current buffer change to "stdin" buffer. This buffer is empty, and still in modified state. I switch back to, try to save it and again empty stdin instead of

    So actually I can not save my rust buffer, I have to set "Rust Format On Save" to off to make possible save, or change mode from rust-mode to text-mode.

    This bug appear only recently, so I suppose it is related to last "rustfmt" changes in master.

    GNU Emacs 26.3 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.10) of 2019-08-29 rust-mode 20200229.1547

  • Package maintainer
    Package maintainer

    Jul 19, 2019

    Since nobody seems to have time for maintaining the package, I would propose that I take care of this. Rustic now has some users and maybe we should suggest to use it instead of rust-mode for emacs26+. However it would be nice to get some help with this(rust-mode + rustic).

    What do you think ?

  • Re-implement rust-in-macro faster
    Re-implement rust-in-macro faster

    Apr 23, 2020

    This function is called repeatedly by syntax-ppss and so needs to be fast. We adopt a simpler mechanism for determining whether we are in a macro.

    Fixes #208