I’ve been getting back into Rust programming for work, and things have changed over the past two years. racer, which I was using for code completion and jump-to-definition, is no longer actively maintained, while LSP and Rustic have appeared on the scene.

A couple weeks ago I decided to set up a fresh Rust config from scratch. It was harder than it should have been, so I’m documenting the final result for others.

Getting the Pre-Requisites

The dependencies should be installed by our Emacs package Rustic:

Simply put (use-package rustic) in your config and most stuff gets configured automatically.

… but aren’t. At least, not on OSX.

So let’s start by installing the components we need.

rustup component add rust-src rustfmt clippy rls rust-analysis

I thought this was all I would need, but of course, that was too naively optimistic of me.

It turns out that rust-analysis is not the same as rust-analyzer, and you need the second one. And neither rustup nor our Rust package actually install it, nor is it available with cargo install. In fact, you have to manually install it. The Rustic instructions don’t mention this, of course, so I had to spend an hour Googling it.

An issue has been filed about this (and summarily ignored).

Emacs Config

Now to the Emacs config. I use Rustic, which purports to be an upgrade to rust-mode and claims to set up dependencies automatically (although it doesn’t, which is why we needed the above section).

;; Enhanced Rust mode with automatic LSP support.
(use-package rustic
:config
(setq
;; eglot seems to be the best option right now.
rustic-lsp-client 'eglot
rustic-format-on-save nil
;; Prevent automatic syntax checking, which was causing lags and stutters.
eglot-send-changes-idle-time (* 60 60)
)
;; Disable the annoying doc popups in the minibuffer.
(add-hook 'eglot-managed-mode-hook (lambda () (eldoc-mode -1)))
)

It doesn’t look like much, but it took me hours to get that right.

I use eglot, because the alternative, lsp-mode, was breaking for some of my (simple) Rust projects, and I couldn’t figure out why after a couple of hours of looking into it. eglot seemed stable, at least.

rustic-lsp-client 'eglot

Here is a post from the maintainer of eglot explaining why his package is better than lsp-mode. I agree with it. Personally, my experience with lsp-mode was awful, but YMMV.

Next, I disabled the automatic code checking, which would run as I typed. This was causing a lot of lag and stutters, all of which was seriously interfering with my programming. This should be an easy thing to turn off, but there’s no option for it. The best we can do is increase the delay; the code checking still runs on save.

eglot-send-changes-idle-time (* 60 60)

Finally, I needed to disable the doc popups in the minibuffer as they were extremely distracting. Again, it took lots of googling to get this right because there was no obvious way to do this.

(add-hook 'eglot-managed-mode-hook (lambda () (eldoc-mode -1)))

Conclusion

Looking back, maybe I should have just stuck with rust-mode. But I am happy with the features I have now: code-completion, jump-to-definition, and syntax-checking on save. They work really well, and after some configuration they don’t get in the way.