Educe
This crate offers procedural macros designed to facilitate the swift implementation of Rust's built-in traits.
Install / Use
/learn @magiclen/EduceREADME
Educe
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
himalaya
345.4kCLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).
coding-agent
345.4kDelegate coding tasks to Codex, Claude Code, or Pi agents via background process
tavily
345.4kTavily web search, content extraction, and research tools.
diffs
345.4kUse the diffs tool to produce real, shareable diffs (viewer URL, file artifact, or both) instead of manual edit summaries.
