Generate Enum Variant with associated values in Rust Analyzer

I’ve continued contributing to Rust Analyzer, and recently implemented a new feature that I’m exited to share with y’all: the “Generate Enum Variant” assist now supports associated values!

TL;DR:

Generate Enum Variant used to ignore associated tuples, and wasn’t available at all if an associated record was present:

An example of using the "Generate Enum Variant" assist, showing associated values being ignored in the generated code, and "No code actions available" for an enum with an associated record. A "Sad Face" emoji is superimposed on the image.

Now it supports both associated tuples and records:

Using the updated "Generate Enum Variant" assist on the same code as the previous example, showing both tuple and record associated values being generated correctly. A "Heart Eyes" emoji is superimposed on the image.

Do What I Mean

Rust analyzer has a category of assists that “generate” boilerplate code. These are great! Not having to type things by hand and instead having my IDE just “do what I mean” is a major speedup. Or, perhaps more accurately, 10,000 tiny speedups.

I find myself using these assists fairly often, especially when I’m doing TDD. A common suggestion for doing Test Driven Development is “write the API you wish you had”. The next step is usually “make it compile”, which is where “generate” style assists come in handy.

Say you’re written some new code, and it’s all red and squiggly. Hitting one or more “Generate…” assists will solve your syntactical sadness and set you up to focus on “getting to green”:

enum DomainModel {}

#[test]
fn the_system_produces_value() {
    let value = business_logic(DomainModel::UseCase);

    assert_eq!(9000, value);
}

Running “Generate Enum Variant” on UseCase updates the DomainModel:

enum DomainModel {
    UseCase,
}

And "Generate Function" spits out:

fn business_logic(use_case: DomainModel) -> i32 {
    todo!()
}

Unfortunately, we can’t (yet?) generate function implementations, so you’re still on the hook to make your code, you know, actually work 😉.

Upgrades

One of the most powerful features of Enums in rust is their ability to carry “associated values”. This is how both Options and Results work, and is a fixture of every rust codebase I’ve encountered. It’s also the feature I most long for when working in a language that doesn’t have it cough typescript cough.

When the “Generate Enum Variant” assist was originally implemented by @fasterthanlime and @maartenflippo, it didn’t support this feature. It would ignore any associated data entirely at the call site, and if you tried to write an enum variant with an associated “record”, the assist wasn’t even available 😢. No worries, gotta start somewhere! Even without this functionality, “Generate Enum Variant” has saved me time and typing!

Three cheers for incrementalism!

Now, when we’re presented with a new requirement:

#[test]
fn the_new_requirement() {
    let value = business_logic(DomainModel::NewRequirement {
        with: "associated values".to_string(),
        like_this: true,
    });
    assert_eq!(9001, value);
}

"Generate Enum Variant" has got our back:

enum DomainModel {
    UseCase,
    NewRequirement { with: String, like_this: bool },
}

Making it happen

Here’s how it all went down:

I think my favorite part of this style of assist is how nicely they compose together. From a single call site, we can generate a variant with associated data, convert that data to a named struct, generate a From impl, and is_, as_ or try_into methods. Magic!

In conclusion, don’t write code when the computer can write it for you.

"Generate all the things!"

Discuss on r/rust