The Rust LSP That Uses 80MB Instead of 8GB
Every developer knows the moment: your laptop fan spins up like it’s preparing for takeoff. Check Activity Monitor, and it’s almost always the language server. In Rust, that name has been rust-analyzer for years. Now a new LSP claiming to use 1/100th the memory has reignited an argument the industry has been having since the day IDEs got smart: how did our editors get this heavy, and did we ever agree to it?
One caveat up front. This is a structural argument, not a poll of community sentiment — the discussion around these tools is still thin enough that citing vote counts would be dressing up noise as data. What follows is about what LSP architecture and incremental compilation actually force you to trade. I’ll flag where the engineering facts end and my read begins.
Why rust-analyzer Eats RAM
rust-analyzer’s memory footprint isn’t a bug. It’s the design working as intended.
Recompiling an entire project on every keystroke would make the editor unusable. So rust-analyzer breaks analysis into fine-grained queries and keeps every result in memory. The machinery behind this is salsa, an incremental computation framework. Change one file, and only the queries that depend on that file get invalidated. Everything else comes straight from cache.
That’s why type inference, autocomplete, go-to-definition, and rename feel instant. The price is blunt: the intermediate representation of your entire project lives in RAM. Multi-gigabyte reports from dependency-heavy projects are routine, and the arithmetic is obvious once you look at it. Pull in a few hundred crates and you’re holding type information for the standard library plus every transitive dependency.
So the RAM rust-analyzer consumes isn’t waste. It’s a trade. You hand over memory and you buy latency. Whether that’s a good deal depends entirely on the machine you’re sitting at.
What 100x Actually Buys — and Costs
The new lightweight servers mostly take the same route to their numbers: they stop trying to know everything.
A full-fat language server does roughly three things. It parses files into a syntax tree. It resolves names to their actual definitions. And it runs type inference to completion, including trait resolution. The memory hog is overwhelmingly the third one.
Rust makes type inference especially expensive, thanks to generics and the trait system. Resolving a single foo.collect() correctly means working backward from context to figure out what the return type is supposed to be. That is not meaningfully cheaper than running the compiler frontend. And once you’ve paid for that answer, throwing it away means paying again on the next keystroke — so it goes in the cache and stays there.
The 100x-lighter servers draw the line right there. They hold the syntax tree and a symbol index, and either skip full type inference entirely or compute it on demand at the call site. The consequences follow directly:
- Document symbols, go-to-definition on direct references, and symbol search work fine. Often faster.
- Autocomplete mid-method-chain degrades badly. No type means no idea what to suggest.
- Go-to-definition through a trait impl, type-aware rename, and inlay hints get unreliable or disappear.
- Diagnostics get handed back to the compiler. You’re running
cargo checkon save, like it’s 2018.
Which means 100x is another way of saying “fewer features.” No one found a magic optimization that does identical work with 1% of the resources. The work itself is different. Any benchmark that blurs this distinction deserves your suspicion.
So Why Is Anyone Excited?
Fair question. Fewer features, and yet the reaction is real. Three reasons.
Most people use fewer features than they think. When you’re reading logs or spelunking through someone else’s crate, go-to-definition and symbol search cover almost everything you need. Keeping an 8GB process resident for that is hard to justify.
Hardware variance is enormous. On a 64GB workstation, you will never notice rust-analyzer’s weight. On a 16GB laptop running 40 browser tabs, Docker, and Slack, the story inverts completely. The instant swap starts, “fast autocomplete” stops meaning anything. Remote dev containers and small cloud instances are worse still.
Nobody runs just one anymore. The old model was one editor, one language server. Now developers keep multiple worktrees checked out and open side by side. Three projects means three language servers, and at 2GB each that’s 6GB gone before you’ve typed anything. At that point the multiplier matters more than the absolute number.
The AI Agent Shift
Here’s the part that actually changed the calculus: humans aren’t the only ones reading the codebase anymore.
Strip an AI coding agent down to what it does, and it’s code navigation. Find where this function is called. Check what this type is defined as. Skim the related files. A human does this one lookup at a time. An agent does it dozens of times in a row — and increasingly, several sessions do it simultaneously.
That inverts the requirements.
A human-facing LSP is optimized for one thing: getting the completion popup on screen within 50ms of a keystroke. For that single goal, precomputing everything and holding it in cache is entirely rational. A human only looks at one place at a time.
An agent needs the opposite: hundreds of queries served broadly and cheaply. Whether a lookup takes 50ms or 200ms is irrelevant when model inference downstream takes several seconds. What actually matters is that ten concurrent sessions don’t take the machine down.
Which is why I’d read these lightweight servers as targeting a different market, not competing with rust-analyzer. Sitting in an editor assisting a human’s typing and serving as an agent’s navigation backend are different jobs with different constraints. It’s the same reason today’s coding tools navigate codebases with regex search plus a symbol index — incomplete type information is enough for most navigation.
What to Actually Run
The answer depends on your situation, and it’s not close.
Keep rust-analyzer if you write Rust all day, work in code thick with generics and trait bounds, and have RAM to spare. Writing Rust without type information is a genuine loss. Trait-resolution-dependent completion in particular has no substitute.
Look at the lightweight option if Rust isn’t your primary language and you mostly read other people’s crates. Or you’re on a remote dev environment or a constrained machine. Or you keep several projects open at once. Or you need a navigation backend for an agent.
Run both — rust-analyzer on the project you’re actively editing, something light on repos you’ve opened for reference. The cost is a messier editor config, which is a real cost, just a small one.
Worth noting that the rust-analyzer team isn’t standing still. Memory reduction has been a long-running concern, and work on cache eviction policy and trimming unused data has continued for years. But architecture sets a floor. Once you’ve committed to holding whole-project type information in RAM, getting to 100x means reversing that decision, not tuning around it.
The Takeaway
This argument was never about who built the better tool. It’s about whether we’re still pricing the RAM-for-latency trade at an exchange rate set when RAM was expensive. A judgment made in an era of one editor and one developer shouldn’t automatically survive into an era of ten agents crawling the same codebase in parallel.
So when a 100x number shows up, the useful question isn’t how they did it. It’s what got thrown out to get there. If it’s features you never used, that’s an excellent trade. If it’s features you did use, that’s just a downgrade with better marketing.
Go check how much RAM your language server is using right now, then count how many of its features you actually touched today. The answer tends to arrive fast.
Comments
Loading comments...