Support Ukraine. DONATE.
A blog about software development.

Kinded 0.5.0 – Generate Rust Enum Kind Types Without Boilerplate

Serhii Potapov February 03, 2026 #rust #enum #macro #kinded

I'm happy to announce the release of Kinded v0.5.0! This update brings several useful features, including const support for the kind() method and flexible attribute handling. You can find the full release notes on GitHub.

What is Kinded?

Kinded is a Rust crate that generates "kind" enums from your existing Rust enums using a derive macro. A kind enum is a simplified version of your enum that contains only the variant names, without any associated data.

This is useful when you need to work with enum variants as values — for example, in configuration, pattern matching, or when you need to compare or display variant names without caring about the data they hold.

Quick Example

Here's how simple it is to use Kinded:

use kinded::Kinded;

#[derive(Kinded)]
enum Drink {
    Mate,
    Coffee(String),
    Tea { variety: String, caffeine: bool }
}

let drink = Drink::Coffee("Espresso".to_owned());
assert_eq!(drink.kind(), DrinkKind::Coffee);

The #[derive(Kinded)] macro automatically generates a DrinkKind enum and a kind() method. Behind the scenes, you get something like this:

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DrinkKind {
    Mate,
    Coffee,
    Tea
}

impl Drink {
    const fn kind(&self) -> DrinkKind {
        match self {
            Drink::Mate => DrinkKind::Mate,
            Drink::Coffee(..) => DrinkKind::Coffee,
            Drink::Tea { .. } => DrinkKind::Tea,
        }
    }
}

The generated kind enum comes with implementations of Debug, Clone, Copy, PartialEq, Eq, Display, FromStr, and From traits out of the box.

What's New in v0.5.0

Const kind() Method

The kind() method is now a const fn, allowing it to be used in const contexts:

use kinded::Kinded;

#[derive(Kinded)]
enum Status {
    Active,
    Inactive,
}

const ACTIVE_KIND: StatusKind = Status::Active.kind();

This is particularly useful when you need to work with enum kinds at compile time, such as in const assertions or static configurations.

The skip_derive Attribute

Sometimes you need to opt out of certain default trait implementations. For example, when using Kinded with crates like enumset that provide their own trait implementations, you can use skip_derive(..) to avoid conflicts:

use kinded::Kinded;

#[derive(Kinded)]
#[kinded(skip_derive(Display, FromStr))]
enum Task {
    Download { url: String },
    Process(Vec<u8>),
}

// Display and FromStr are not implemented for TaskKind
// You can provide your own custom implementations if needed

The following traits can be skipped:

Generic Attributes with attrs

If you're using derive traits from other libraries like Serde, you might want to add extra attributes specific to those libraries. The new attrs attribute makes this possible:

use kinded::Kinded;
use serde::Serialize;

#[derive(Kinded, Serialize)]
#[kinded(display = "snake_case", attrs(serde(rename_all = "snake_case")))]
enum Drink {
    VeryHotBlackTea,
    Milk { fat: f64 },
}

let json_value = serde_json::to_value(&DrinkKind::VeryHotBlackTea);
assert_eq!(json_value.unwrap(), serde_json::json!("very_hot_black_tea"));

Per-Variant Attributes

You can also apply attributes to individual variants of the generated kind enum:

use kinded::Kinded;

#[derive(Kinded)]
#[kinded(derive(Default))]
enum Priority {
    Low,
    #[kinded(attrs(default))]
    Medium,
    High,
}

// Medium is the default
assert_eq!(PriorityKind::default(), PriorityKind::Medium);

This combines well with the rename attribute introduced in v0.4.1:

#[derive(Kinded)]
#[kinded(derive(Default))]
enum Level {
    #[kinded(rename = "low_level", attrs(default))]
    Low,
    Medium,
}

Other Features

Kinded offers additional capabilities that make working with Rust enums more convenient:

Back to top