Rust-ing into Open Source: My debut with Rust-Analyzer
I've made my first open-source contribution to Rust. It's on rust-analyzer
- the compiler front-end for IDEs like VS Code. The code completion list now shows constructors and builder methods first.
The PR has been merged, therefore it's available in the nightly version. It should land on the next preview version of rust-analyzer
soon (Update: This is now in release 2024-02-19).
Motivation
I want to be able to determine if a type can be created quickly; the established norm is to look for constructors with names new
, from_
etc. or a builder
 methods.
Norms aren't rules; there are exceptions too: PathBuf
and Vec
has with_capacity
, they are well known but what if the library author needs to name the constructor something else?
Naming is hard, and no "one rule fits all (libraries)" when naming the constructors. Instead of looking for these franticly in the completion list, what if the IDE presented those methods first? After all, when I enter YourType::
I am primarily interested in creating an instance of this type. To be precise, I am looking for
- Direct Constructors: Method that creates an instance of
Self
that may or may not take an argument. eg.fn new() -> Self
- Constructors: similar to direct constructors but
Self
is wrapped, a typical example isResult<T, E>
,Option<T>
orSelf
wrapped in other types. - Builder methods: These are methods that return a different type that typically ends in
Builder
, eg.
struct MyType;
struct MyTypeBuilder;
impl MyType {
fn builder() -> MyTypeBuilder {}
}
Document it
My first attempt at addressing this was to update rustdoc
generator to highlight constructors & builder methods in the generated documentation.
However, I lost motivation even before I got started, as evidenced by this PR. The reason being:
- If I'm browsing the documentation, there's a good chance I tried to find a way to create an instance of this type in the IDE first.
- It's easy to forget the name of the constructors even if I've used the type before. So #1 applies here too.
I did not want to do something I didn't think would be helpful. So I dropped the idea and took a long pause (I got busy with life) until the quiet period of the year, December, arrived. That is when I started putting some late-night (after my daughter is asleep till I'm too tired to think) digging into the rust-analyzer
.
Implement it where it's most useful
This was the exciting part because although I write a lot of rust code in my day job, These are CRUD web applications and event-based distributed systems designed to process payments. On the other hand, the Rust analyser is a complex monolith, a highly optimised language server intended not to get in my way when I'm building other Rust applications (while being the second best friend - after rustc
). This is nothing like what I do during the day, in other words, when it's gloomy outside (in an English village).
I'll be honest, the first implementation wasn't elegant at all. It was keen to put something together & validate the idea from the rust-analyzer
contributors before iterating on the implementation. Given the limited time I could put into this outside work, this seems the best approach.
I am so grateful to the reviewer, Lukas Wirth, for all the pointers in the PR. The few iterations made the implementation more elegant and up to the mark, given a language server's strict performance/overhead expectations.
What's next?
I have a few ideas I'd like to try for rust-analyzer
and this contribution will inspire me to strive in that direction. I may post about these on my blog (or not, depending on how excited I am for these or how they turn out).
Conclusion
I'm glad my first contribution to Rust language is in one of the frequently used features of rust-analyzer
- code completion.
Every time you enter a type in the IDE, the code completion list appears- and if the first item isn't a constructor or builder method, you immediately know you should RTFM; there is no point in looking for a constructor.