A little tip to improve rust-analyzer memory usage

Posted on September 18, 2023

Note: I am new to Rust, so please pardon me if I am doing something stupid.

rust-analyzer is an excellent companion when you are writing Rust code. If you don’t know what rust-analyzer is, it is the engine or LSP Server that powers editors like VSCode (or VSCodium), Emacs, Vim etc. to help you write Rust with auto-completion, debugging and refactoring support.

However, it is one of the worst offenders when it comes to memory efficiency. It sucks up more memory than all Electron apps combined. You can do a quick search on GitHub which reveals how common this issue is.

On one of my projects I had to generate some FFI bindings to some C code using bindgen. FFI bindings are generated by using build.rs file. Every time I do cargo build, bindings are generated. Then I consume these bindings in my Rust code.

The structure of my project looked something like this

build.rs
Cargo.lock
Cargo.toml
src/
    main.rs
    lib.rs
wrapper.h

build.rs consumes wrapper.h to generate bindings which are then included in src/lib.rs.

Pretty simple, right? However, rust-analyzer does not like this setup. If I opened this project in an editor, it would pile up the memory and eventually crash my VM. I tried VSCode and Emacs with lsp-mode. Nothing worked. It seems that there is a serious memory leak bug in rust-analyzer. However, this did not happen on a simple hello-world project. So I suspected this could be due to bindings being generated during cargo build or cargo check.

Then I read about Cargo Workspaces which is a feature in Cargo to split a bigger crate into multiple packages and link them through a master Cargo.toml file. I thought about whether this would help with my situation.

I separated the binding generation part and the consuming part into two packages using Cargo Workspaces. Surprisingly it worked! rust-analyzer stopped piling up the memory. I still don’t know the root cause for the memory leaks, but at least I am able to use my VM and write some code.

The new project structure looks like this

Cargo.lock
Cargo.toml
bindings-package/
    src/
        lib.rs	  
    build.rs
    Cargo.toml
    wrapper.h
consumer-package/
    src/
        main.rs
    Cargo.toml

This structure also improved cargo build time. If you are facing high memory usage with rust-analyzer when using FFI bindings or a heavier build.rs script, try splitting it into separate packages with Cargo Workspaces. It might help you.