SkillAgentSearch skills...

Educe

This crate offers procedural macros designed to facilitate the swift implementation of Rust's built-in traits.

Install / Use

/learn @magiclen/Educe
About this skill

Quality Score

0/100

Category

Design

Supported Platforms

Universal

Tags

README

Educe

CI

This crate offers procedural macros designed to facilitate the swift implementation of Rust's built-in traits.

Features

By default, every trait this crate supports will be enabled. You can disable all of them by turning off the default features and enable only the traits that you want to use by adding them to the features explicitly.

For example,

[dependencies.educe]
version = "*"
features = ["Debug", "Clone", "Copy", "Hash", "Default"]
default-features = false

Traits

Debug

Use #[derive(Educe)] and #[educe(Debug)] to implement the Debug trait for a struct, enum, or union. This allows you to modify the names of your types, variants, and fields. You can also choose to ignore specific fields or set a method to replace the Debug trait. Additionally, you have the option to format a struct as a tuple and vice versa.

Basic Usage
use educe::Educe;

#[derive(Educe)]
#[educe(Debug)]
struct Struct {
    f1: u8
}

#[derive(Educe)]
#[educe(Debug)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
Change the Name of a Type, a Variant or a Field

The name parameter can rename a type, a variant or a field. If you set it to false, the name can be ignored or forced to show otherwise.

use educe::Educe;

#[derive(Educe)]
#[educe(Debug(name(Struct2)))]
struct Struct {
    #[educe(Debug(name(f)))]
    f1: u8
}

#[derive(Educe)]
#[educe(Debug(name = true))]
enum Enum {
    #[educe(Debug(name = false))]
    V1,
    #[educe(Debug(name(V)))]
    V2 {
        #[educe(Debug(name(f)))]
        f1: u8,
    },
    #[educe(Debug(name = false))]
    V3(u8),
}
Ignore Fields

The ignore parameter can ignore a specific field.

use educe::Educe;

#[derive(Educe)]
#[educe(Debug)]
struct Struct {
    #[educe(Debug(ignore))]
    f1: u8
}

#[derive(Educe)]
#[educe(Debug)]
enum Enum {
    V1,
    V2 {
        #[educe(Debug(ignore))]
        f1: u8,
    },
    V3(
        #[educe(Debug(ignore))]
        u8
    ),
}
Fake Structs and Tuples

With the named_field parameter, structs can be formatted as tuples and tuples can be formatted as structs.

use educe::Educe;

#[derive(Educe)]
#[educe(Debug(named_field = false))]
struct Struct {
    f1: u8
}

#[derive(Educe)]
#[educe(Debug)]
enum Enum {
    V1,
    #[educe(Debug(named_field = false))]
    V2 {
        f1: u8,
    },
    #[educe(Debug(named_field = true))]
    V3(
        u8,
        #[educe(Debug(name(value)))]
        i32
    ),
}
Use Another Method to Handle the Formatting

The method parameter can be utilized to replace the implementation of the Debug trait for a field, eliminating the need to implement the Debug trait for the type of that field.

use educe::Educe;

use std::fmt::{self, Formatter};

fn fmt<T>(_s: &T, f: &mut Formatter<'_>) -> fmt::Result {
    f.write_str("Hi")
}

#[derive(Educe)]
#[educe(Debug)]
enum Enum<T> {
    V1,
    V2 {
        #[educe(Debug(method(fmt)))]
        f1: u8,
    },
    V3(
        #[educe(Debug(method(std::fmt::UpperHex::fmt)))]
        u8,
        #[educe(Debug(method(fmt)))]
        T
    ),
}
Generic Parameters Bound to the Debug Trait or Others

Generic parameters will be automatically bound to the Debug trait if necessary.

use educe::Educe;

#[derive(Educe)]
#[educe(Debug)]
enum Enum<T, K> {
    V1,
    V2 {
        f1: K,
    },
    V3(
        T
    ),
}

Or you can set the where predicates by yourself.

use educe::Educe;

use std::fmt::{self, Formatter};

fn fmt<D>(_s: &D, f: &mut Formatter<'_>) -> fmt::Result {
    f.write_str("Hi")
}

#[derive(Educe)]
#[educe(Debug(bound(T: std::fmt::Debug)))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(Debug(method(fmt)))]
        f1: K,
    },
    V3(
        T
    ),
}

In the above case, T is bound to the Debug trait, but K is not.

Or, you can have educe replicate the behaviour of std's derive's, where a bound is produced for every generic parameter, without regard to how it's used in the structure:

use educe::Educe;

#[derive(Educe)]
#[educe(Debug(bound(*)))]
struct Struct<T> {
    #[educe(Debug(ignore))]
    f: T,
}

This can be useful if you don't want to make the trait implementation part of your permanent public API. In this example, Struct<T> doesn't implement Debug unless T does. I.e., it has a T: Debug bound even though that's not needed right now. Later we might want to display f; we wouldn't then need to make a breaking API change by adding the bound.

This was the behaviour of Trait(bound) in educe 0.4.x and earlier.

Union

A union will be formatted as a u8 slice because we don't know its fields at runtime. The fields of a union cannot be ignored, renamed, or formatted with other methods. The implementation is unsafe because it may expose uninitialized memory.

use educe::Educe;

#[derive(Educe)]
#[educe(Debug(unsafe))]
union Union {
    f1: u8,
    f2: i32,
}

Clone

Use #[derive(Educe)] and #[educe(Clone)] to implement the Clone trait for a struct, an enum, or a union. You can set a method to replace the Clone trait.

Basic Usage
use educe::Educe;

#[derive(Educe)]
#[educe(Clone)]
struct Struct {
    f1: u8
}

#[derive(Educe)]
#[educe(Clone)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
Use Another Method to Perform Cloning

The method parameter can be utilized to replace the implementation of the Clone trait for a field, eliminating the need to implement the Clone trait for the type of that field.

use educe::Educe;

fn clone(v: &u8) -> u8 {
    v + 100
}

trait A {
    fn add(&self, rhs: u8) -> Self;
}

fn clone2<T: A>(v: &T) -> T {
    v.add(100)
}

#[derive(Educe)]
#[educe(Clone)]
enum Enum<T: A> {
    V1,
    V2 {
        #[educe(Clone(method(clone)))]
        f1: u8,
    },
    V3(
        #[educe(Clone(method(clone2)))]
        T
    ),
}
Generic Parameters Bound to the Clone Trait or Others

Generic parameters will be automatically bound to the Clone trait if necessary. If the #[educe(Copy)] attribute exists, they will be bound to the Copy trait.

use educe::Educe;

#[derive(Educe)]
#[educe(Clone)]
enum Enum<T, K> {
    V1,
    V2 {
        f1: K,
    },
    V3(
        T
    ),
}

Or you can set the where predicates by yourself.

use educe::Educe;

trait A {
    fn add(&self, rhs: u8) -> Self;
}

fn clone<T: A>(v: &T) -> T {
    v.add(100)
}

#[derive(Educe)]
#[educe(Clone(bound(T: std::clone::Clone)))]
enum Enum<T, K: A> {
    V1,
    V2 {
        #[educe(Clone(method(clone)))]
        f1: K,
    },
    V3(
        T
    ),
}

In the above case, T is bound to the Clone trait, but K is not.

Or, you can have educe replicate the behaviour of std's derive's by using bound(*). See the Debug section for more information.

use educe::Educe;

trait A {
    fn add(&self, rhs: u8) -> Self;
}

fn clone<T: A>(v: &T) -> T {
    v.add(100)
}

#[derive(Educe)]
#[educe(Clone(bound(*)))]
struct Struct<T: A> {
    #[educe(Clone(method(clone)))]
    f: T,
}
Union

Refer to the introduction of the #[educe(Copy)] attribute.

Copy

Use #[derive(Educe)] and #[educe(Copy)] to implement the Copy trait for a struct, an enum, or a union.

Basic Usage
use educe::Educe;

#[derive(Educe)]
#[educe(Copy, Clone)]
struct Struct {
    f1: u8
}

#[derive(Educe)]
#[educe(Copy, Clone)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
Generic Parameters Bound to the Copy Trait or Others

All generic parameters will be automatically bound to the Copy trait.

use educe::Educe;

#[derive(Educe)]
#[educe(Copy, Clone)]
enum Enum<T, K> {
    V1,
    V2 {
        f1: K,
    },
    V3(
        T
    ),
}

Or you can set the where predicates by yourself.

use educe::Educe;

trait A {
    fn add(&self, rhs: u8) -> Self;
}

fn clone<T: A>(v: &T) -> T {
    v.add(100)
}

#[derive(Educe)]
#[educe(Copy, Clone(bound(T: Copy, K: A + Copy)))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(Clone(method(clone)))]
        f1: K,
    },
    V3(
        T
    ),
}

Note that utilizing custom cloning methods for a type that implements the Copy and Clone traits may not be entirely appropriate.

Union

The #[educe(Copy, Clone)] attribute can be used for a union. The fields of a union cannot be cloned with other methods.

use educe::Educe;

#[derive(Educe)]
#[educe(Copy, Clone)]
union Union {
    f1: u8,
}

PartialEq

Use #[derive(Educe)] and #[educe(PartialEq)] to implement the PartialEq trait for a struct, enum, or union. You can also choose to ignore specific fields or set a method to replace the PartialEq trait.

Basic Usage
use educe::Educe;

#[derive(Educe)]
#[educe(PartialEq)]
struct Struct {
    f1: u8
}

#[derive(Educe)]
#[educe(PartialEq)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
Ignore Fields

The ignore parameter can ignore a specific field.

use educe::Educe;

#[derive(Educe)]
#[educe(PartialEq)]
struct Struct {
    #[educe(PartialEq(ignore))]
    f1: u8
}

#[derive(Educe)]
#[educe(PartialEq)]
enum Enum {
    V1,
    V2 {
        #[educe(PartialEq(ignore))]
        f1: u8,
    },
    V3(
        #[educe(PartialEq(ignore))]
        u8
    ),
}
Use Another Method to Perform Comparison

The method parameter can be utilized to replace the implementation of the PartialEq trait for a field, eliminating the need to implement the PartialEq trait for the type of that field.

use educe::Educe;

fn eq(a: &u8, b: &u8) -> bool {
    

Related Skills

View on GitHub
GitHub Stars156
CategoryDesign
Updated1mo ago
Forks17

Languages

Rust

Security Score

95/100

Audited on Feb 26, 2026

No findings