Use rust-analyzer + ALE to show errors as you code in Rust
The more advanced you become as a developer the more you realize that the speed of coding is not just about language syntax or typing, it's as much about the tools and techniques you use.
One of my best Python coding setup tweaks has been showing errors upon saving files, for which I use this plugin. This speeds up development significantly! π π
I wanted to do the same for Rust coding, specially because there is, compared to Python, an extra compilation step in Rust.
In this article I'll show you how I have set it up ...
Note that I use Vim as my text editor and Mac as my operating system. I hope most of the setup is easily transferable to other text editors and operating systems. Or that at least it gets you thinking about how you can speed up your coding workflow. π‘
Install rust-analyzer
First, you need to install rust-analyzer
. I did this with brew
:
brew install rust-analyzer
Install ALE, the Asynchronous Lint Engine
I use Vundle as my Vim plugin manager so I added this plugin to my .vimrc
:
Plugin 'dense-analysis/ale'
And installed it with:
:PluginInstall
Setup in .vimrc
I added the following code to my .vimrc
. I learned I can use ALE for Python as well in one go :)
" ALE Configuration
" Enable ALE for Rust and Python
let g:ale_linters = {
\ 'rust': ['analyzer'],
\ 'python': ['pyflakes'],
\}
" Only lint on save, not on text changed or insert leave
let g:ale_lint_on_text_changed = 'never'
let g:ale_lint_on_insert_leave = 0
let g:ale_lint_on_enter = 0
let g:ale_lint_on_save = 1
" Ensure rust-analyzer is installed and in your PATH
let g:ale_rust_analyzer_executable = 'rust-analyzer'
-
The
ale_linters
variable is set to userust-analyzer
for Rust files andpyflakes
for Python files. I installedpyflakes
withpipx
. By the way, this might become a replacement for the Python plugin I mentioned in the beginning, not sure yet ... -
The
ale_lint_on_save
variable is set to1
to check the syntax on save. -
The
ale_rust_analyzer_executable
variable is set torust-analyzer
to ensure thatrust-analyzer
is installed and in your PATH. -
The
ale_lint_on_text_changed
,ale_lint_on_insert_leave
, andale_lint_on_enter
variables are set tonever
,0
, and0
respectively to prevent linting on text changed, insert leave, and enter. Tweak these settings as you see fit.
Playing around with this plugin I added some more settings to my .vimrc
:
" Enable ALE's virtual text feature for inline messages
let g:ale_virtualtext_cursor = 1
let g:ale_virtualtext_prefix = 'β '
" Customize the ALE sign column for better readability
let g:ale_sign_error = '>>'
let g:ale_sign_warning = '--'
" Enable ALE to use the quickfix list
let g:ale_open_list = 1
let g:ale_set_quickfix = 1
" Enable line wrapping only in quickfix and loclist buffers
autocmd FileType qf setlocal wrap linebreak
autocmd FileType loclist setlocal wrap linebreak
" Enable ALE's floating window preview feature to show detailed error messages
let g:ale_detail_to_floating_preview = 1
See it in action
Here is some code to try this on:
mod data;
use tokio;
use crate::data::fetch_data;
#[tokio::main]
async fn main() {
let data = fetch_data().await.unwrap();
println!("{:#?}", data);
}
Let's make a couple of errors and see the ALE checker in action π‘ π
- changing
mod data;
tomod data
E mod data // E: Syntax Error: expected `;` or `{`
data
todata2
which is not defined
E println!("{:#?}", data2); // E: cannot find value `data2` in this scope
- removing a
use
statement
W use tokio; // W: consider importing this function: `use crate::data::fetch_data; `
...
E let data = fetch_data().await.unwrap(); // E: cannot find function `fetch_data` in this scope not found in this sc
- removing the
#[tokio::main]
attribute
W use tokio; // W: remove the whole `use` item
E async fn main() { // E: `main` function is not allowed to be `async` `main` function is not allowed to be `async`
- removing the
async
keyword
E fn main() { // E: the `async` keyword is missing from the function declaration
Specially the ale_open_list
and ale_set_quickfix
settings are useful. The quickfix panel shows all the errors in a separate window, which is useful when you have mulitple errors that wrap in the editor window. For example:
And it also works for Python π π
...
E print(c) # E: undefined name 'c'
And:
E print(a # E: '(' was never closed
Complementing with GitHub Copilot
If an error is not 100% you can always add a "question comment" like this to get more suggestions. Here for example I purposely removed the await
method from the fetch_data
function:
...
async fn main() {
E let data = fetch_data().unwrap(); // E: no method named `unwrap` found for opaque type `impl Future<Output = Resulβ¦
println!("{:#?}", data);
}
# q: why does the above fail?
GitHub Copilot will suggest the following answer right below the question:
# a: the fetch_data function is async, so it returns a Future, not the data itself
This is a useful technique and faster, because I can stay in Vim rather than making the round trip to ChatGPT. π€ π
Hope this helps you speed up your Rust coding! π¦
Either as a Vim user or not, you can use the same principle to speed up your coding in your favorite text editor.
Or at least get into the mindset of speeding up your coding through efficient tools and techniques. πͺ π
If you want to learn more about my Vim setup overall, check out this video: Supercharge Your Vim Workflow: Essential Tips and Plugins for Efficiency. π₯ π