SATURDAY, AUGUST 29, 2026|No. 13114
Technology · Software

Rust Glancer Offers Low-Memory Alternative for Rust Language Server

A new Rust Language Server implementation, Rust Glancer, aims to significantly reduce memory consumption, offering a potential solution for developers on older or less powerful hardware.

A screenshot illustrating the low memory usage of the Rust Glancer language server.
A screenshot illustrating the low memory usage of the Rust Glancer language server. · Photo by Chris Ried on Unsplash
1 sources
Pipeline ingest
3 reads
Positive / Neutral / Negative
0 countries
Related coverage

I want to present a project that I've been working on for the past 4 months: an alternative Rust LSP implementation that is built with a focus on low memory usage.

It has two main features:

  • It can use very little memory (target Vec { 2 vals.iter().copied().map(|v| v * 2).collect() 3}

The _code_ is pretty simple, but in order to support it we need:

- Slice type support
- Closures / Fn traits
- Trait solving
- Associated type projection
- _A bunch of nightly stuff_

the last item is funny: I wanted to avoid nightly, but I somehow didn't think that `std` (or sysroot in general) _breathes_ nightly. Welp.

So all in all, one feature after another, I slowly was getting from "smart ctags" to a "real LSP".
Probably, the three biggest milestones were:

1. Declarative macro expansion (I hate declarative macros now). Thankfully, I was able to reuse most of rust-analyzer's infrastructure for that.
2. Proper type inference engine. It was a big "oh wow" moment when I truly realized how type inference works (in short: we "link" all related type bindings in a big inference table, and then we try to get evidence from all possible places, where providing evidence can solve types for multiple places). It was the moment that probably brought me the most joy during the work on this project so far.
3. Proper trait solving engine. I initially wrote "it's highly unlikely that we will have a trait solver in this project", but then I _really_ wanted to get the abovementioned iterator example to work properly. I resisted integrating trait solver for a while, trying to have naive hacks like naive trait impl matching + specialized handlers for `std` traits, but it was getting more and more complex while working pretty poorly. Then I gave up and integrated Chalk, which turned out to be significantly simpler than the whole hierarchy I have built. Making Chalk fast was another challenge, though.

Somewhat separately, probably the thing I am most proud of (and the thing that made Rust Glancer possible -- had I not designed it early, the project would die very quickly) is a cool profiling stack that can measure performance, memory usage (both natively, tracking actual allocated objects, and with jemalloc), profile data on demand, and compare LSP against rust-analyzer, as well as a set of benchmarks running in CI. If you're interested, it's partially covered in the docs ( [1](https://rust-glancer.github.io/docs/development/MEMORY.html#memory-tooling--techniques), [2](https://rust-glancer.github.io/docs/development/PROFILING.html)), but I'll work on a more detailed coverage later.

Probably ~1.5 months ago I started using Rust Glancer as my daily driver instead of rust-analyzer. Now, I am happy with its state enough to present it to a larger audience.

## LLM use

This project was built with heavy use of LLMs. It is not vibe coded, though.
I am verifying each pull request to make sure that I am happy with the state of the codebase. If you need proofs, you can check the git history: it has PRs with 10k+ lines of diff, but these are multiple days apart despite the fact that I work on this project nearly every day since its inception. I care about the code, and tbh it would be weird for me to spend 4 months creating a _Rust LSP_ if looking at the code wasn't something I do a lot.

I am not going to pretend that I am an experienced LSP developer and the code is perfect. It is in a state that I can work with, but I understand that some bits might not be idiomatic in terms of compiler tooling design. The code has a lot of comments, and I tried really hard to make sure that these comments are not sloppy but helpful, because _I_ have to read them all the time; so far the quality is obviously not as good as professionally written human docs, but IMHO it's pretty helpful and not annoying to read.

A large part of the journey is learning. LLMs can be pretty good domain experts, and LLMs know about LSP design much more than I do. At the same time, LLMs are not great at building big projects. So the following loop happened multiple times during development:

1. I build something new.
2. LLM proposals seem reasonable, so I go with them.
3. It works but something bugs me.
4. I think about the design for a while and see a big flaw.
5. I work with LLM to fix it (sometimes for a week, if the screw up was particularly big -- but the bigger the screw-up is, the more I learn).

So on one hand, if I am to attribute code ownership to the LLMs, I can complain: "LLMs tried to derail the project so many times!11". But since it's _my_ code, I think that the code might get worse at some moments, but as I learn, I get to improve it. Which is pretty normal software development flow, just accelerated.

All in all, LLMs are just a tool, and it's one's choice to use it responsibly or outsource thinking to it.
Given the amount of witch hunting today, I have just one request: do not reduce me to a clanker. It is my code, so if you consider it to be slop, call it _my_ slop, not AI.

I am open to criticism and will happily listen to feedback: the more I learn, the more I can improve the codebase. Whether I use LLMs for that or not does not matter that much, in my opinion.

## What's next

The project is already in a state where it can be a daily driver for some users, but I have rather big plans for it.
So in the coming releases, you might expect:

- Further performance optimizations
- Some more memory optimizations (primarily during indexing, plus there are a few fragmentation issues happening after a full indexing run that I want to fix)
- Improved type inference / syntax support.
- Code actions (implement missing trait fields, auto-imports, etc).
- Potentially proc macro support (I have some weird idea that will not require actual code execution, but it'll take a while to prepare).

Some features are unlikely to be supported though, such as build scripts / proc macros support via proc macro invocation (e.g. anything that requires untrusted code execution). I also don't plan to work on things that are unnecessary at the current state of the project, such as migrating to the new trait solver. Niche things like particular nightly features will likely be postponed until the project reaches some degree of maturity with stable Rust.

Additionally, there is a lot of cool little tricks I've done in Rust Glancer that I'm somewhat proud of (aligning allocation lifetimes to reduce memory fragmentation, engine-as-a-subprocess model to help with both memory fragmentation and multi-workspace projects, sharded cache, and others), so if people will be interested, I'll be happy to write some blogs telling about how Rust Glancer works under the hood. It's partially covered in the docs already ( [1](https://rust-glancer.github.io/docs/architecture/ARCHITECTURE.html), [2](https://rust-glancer.github.io/docs/development/MEMORY.html)) if you want to get some info right now.

But in any case, I hope that the project can be helpful for some folks already, and for more folks in the future.

PAN's pipeline reviewed approximately 1 open sources for this article. No human editor reviewed this article before publication.

Related Reads

Show on timeline →