Rust程式語言內建的derive屬性可以快速地替我們的型別加上內建的特性,不過會要求該型別內的欄位也都要先實作相同的特性,而且很多時候利用derive屬性實作出來的特性並不能達到我們期望的功能。



好比說以下這個用來作為唯讀資料庫的資料模型的結構體:

#[derive(Hash, PartialEq, Eq)]
pub struct Person {
    uid: u64,
    name: String,
    age: u8
}

我們希望這個Person結構體可以被雜湊,且用有判斷兩個Person結構體是否相同的功能。以上的實作方式雖然在使用起來並沒有什麼太大的問題,但是效能不會很好,因為透過derive屬性實作的Hash特性,會去計算所有欄位的雜湊值,然而在這裡我們其實只要去計算uid這個欄位的雜湊值就可以了。同理,PartialEq特性也是一樣只需要去判斷uid這個欄位是否PartialEq就好了,實在不需要用上所有欄位。

再來,如果我們加了某個沒有同時實作HashPartialEqEq特性的欄位,這個Person結構體就沒有辦法被編譯了。

例如:

#[derive(Hash, PartialEq, Eq)]
pub struct Person {
    uid: u64,
    name: String,
    age: u8,
    score: f64,
}

以上程式,由於型別為f64score欄位沒有實作HashEq特性,所以會編譯失敗。

還有,如果我們的型別欄位有用到泛型的話,泛型型別參數也必須要加上特性的限制,否則也會無法編譯。

例如:

#[derive(Hash, PartialEq, Eq)]
pub struct Person<T> {
    uid: u64,
    name: T,
    age: u8,
}

若以上這個Person結構體要成功使用derive屬性來實作HashPartialEqEq特性的話,要改成以下這樣:

use std::hash::Hash;

#[derive(Hash, PartialEq, Eq)]
pub struct Person<T: Hash + PartialEq + Eq> {
    uid: u64,
    name: T,
    age: u8,
}

然而做這樣的修改可能會使這個結構體想要表現的功能和原本的不同。原本我們可能預期泛型型別參數T就是一個任意的型別,不一定要實作HashPartialEqEq特性,然而將其加上特性的限制之後,T就不能是任意的型別了。為了解決這個問題,我們必須要手動實作特性,並且只在實作時替泛型型別參數加上特性的限制,而不是像以上這樣直接在型別上進行限制。

Educe

「Educe」是筆者開發的程序式巨集套件,可以用來加強原先Rust程式語言用derive屬性來實作內建特性的方式,使得這類程式序巨集可以在更多常見的場景下直接使用,讓開發者不需要手動去寫impl相關的程式。

Crates.io

https://crates.io/crates/educe

Cargo.toml

educe = "*"

使用方法

Educe目前能夠實作的特性有DebugPartialEqEqPartialOrdOrdHashDefaultCloneCopyDerefDerefMut

上面的泛型型別參數例子可以很輕易地用Educe來改寫,程式如下:

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(Hash(bound), PartialEq(bound), Eq(bound))]
pub struct Person<T> {
    uid: u64,
    name: T,
    age: u8,
}
特色

在預設的情形下,Educe會啟用所有有支援的特性。如果想要禁用它們的話,在Cargo.toml設定檔中,可以不啟用預設特性,只把自己想要用的特性加入features中來啟用就好。

如下:

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

在型別上加上#[derive(Educe)]#[educe(Debug)]屬性,就可以替該型別實作Debug特性。型別可以是任意結構體、任意列舉和任意聯合(union)。此外,它還有支援型別名稱、變體名稱和欄位名稱的改變,也能夠忽略掉指定的欄位,還能夠替指定的欄位設定格式化時要使用的方法和(或)特性。更方便的是,它還能夠把一般結構體當作數組結構體(tuple struct)來格式化,或是反過來把數組結構體當作一般結構體來格式化。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(Debug)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
改變型別名稱、變體名稱或是欄位名稱

name屬性可以用來重新命名一個型別、變體或是欄位。

#[macro_use]
extern crate 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屬性可以用來忽略指定的欄位。

#[macro_use]
extern crate 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
    ),
}
數組結構體格式化成一般結構體,一般結構體格式化成數組結構體

named_field屬性可以用來設定型別或是變體下的欄位是否要擁有名稱。

#[macro_use]
extern crate 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
    ),
}
使用另外的方法或特性來做格式化

traitmethod參數可以被用在欄位上,取代該欄位的Debug特性需求。如果您只有設定trait的參數的話,method參數會被自動預設為fmt

#[macro_use]
extern crate educe;

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

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

trait A {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("Hi")
    }
}

impl A for i32 {};
impl A for u64 {};

#[derive(Educe)]
#[educe(Debug)]
enum Enum<T: A> {
    V1,
    V2 {
        #[educe(Debug(method = "fmt"))]
        f1: u8,
    },
    V3(
        #[educe(Debug(trait = "std::fmt::UpperHex"))]
        u8,
        #[educe(Debug(trait = "A"))]
        T
    ),
}
泛型參數自動加上Debug特性限制,或是手動設定其它限制

在型別上加上#[educe(Debug(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作Debug特性的條件下來替型別實作Debug特性。

#[macro_use]
extern crate educe;

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

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

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

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

trait A {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("Hi")
    }
}

impl A for i32 {};
impl A for u64 {};

#[derive(Educe)]
#[educe(Debug(bound = "T: std::fmt::Debug, K: A"))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(Debug(trait = "A"))]
        f1: K,
    },
    V3(
        T
    ),
}
聯合

聯合並不會依照其欄位的型別來做格式化,而是會將聯合在記憶體中的值以u8切片的方式來格式化,因為我們無法在程式執行階段知道當前的聯合究竟是在用哪個欄位。聯合中的欄位無法被省略、重新命名或是用其它的特性或是方法來格式化。

#[macro_use]
extern crate educe;

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

在型別上加上#[derive(Educe)]#[educe(PartialEq)]屬性,就可以替該型別實作PartialEq特性。型別可以是任意結構體和任意列舉。此外,它還能夠忽略掉指定的欄位,也能夠替指定的欄位設定在比較是否相同時要使用的方法和(或)特性。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(PartialEq)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
忽略欄位

ignore屬性可以用來忽略指定的欄位。

#[macro_use]
extern crate 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
    ),
}
使用另外的方法或特性來做比較

traitmethod參數可以被用在欄位上,取代該欄位的PartialEq特性需求。如果您只有設定trait的參數的話,method參數會被自動預設為eq

#[macro_use]
extern crate educe;

fn eq(a: &u8, b: &u8) -> bool {
        a + 1 == *b
}

trait A {
    fn eq(&self, b: &Self) -> bool;
}

impl A for i32 {
    fn eq(&self, b: &i32) -> bool {
        self + 1 == *b
    }
}

impl A for u64 {
    fn eq(&self, b: &u64) -> bool {
        self + 1 == *b
    }
}

#[derive(Educe)]
#[educe(PartialEq)]
enum Enum<T: A> {
    V1,
    V2 {
        #[educe(PartialEq(method = "eq"))]
        f1: u8,
    },
    V3(
        #[educe(PartialEq(trait = "A"))]
        T
    ),
}
泛型參數自動加上PartialEq特性限制,或是手動設定其它限制

在型別上加上#[educe(PartialEq(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作PartialEq特性的條件下來替型別實作PartialEq特性。

#[macro_use]
extern crate educe;

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

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

trait A {
    fn eq(&self, b: &Self) -> bool;
}

impl A for i32 {
    fn eq(&self, b: &i32) -> bool {
        self + 1 == *b
    }
}

impl A for u64 {
    fn eq(&self, b: &u64) -> bool {
        self + 1 == *b
    }
}

#[derive(Educe)]
#[educe(PartialEq(bound = "T: std::cmp::PartialEq, K: A"))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(PartialEq(trait = "A"))]
        f1: K,
    },
    V3(
        T
    ),
}
Eq

在型別上加上#[derive(Educe)]#[educe(Eq)]屬性,就可以替該型別實作Eq特性。型別可以是任意結構體、任意列舉和任意聯合(union)。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(PartialEq, Eq)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
泛型參數自動加上Eq特性限制,或是手動設定其它限制

在型別上加上#[educe(Eq(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作Eq特性的條件下來替型別實作Eq特性。

#[macro_use]
extern crate educe;

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

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

trait A {
    fn eq(&self, b: &Self) -> bool;
}

impl A for i32 {
    fn eq(&self, b: &i32) -> bool {
        self + 1 == *b
    }
}

impl A for u64 {
    fn eq(&self, b: &u64) -> bool {
        self + 1 == *b
    }
}

#[derive(Educe)]
#[educe(PartialEq(bound = "T: std::cmp::PartialEq, K: A"), Eq(bound = "T: std::cmp::PartialEq, K: A"))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(PartialEq(trait = "A"))]
        f1: K,
    },
    V3(
        T
    ),
}

Eq特性相依於PartialEq(PartialEq<Self>)特性,在手動限制泛型型別參數要實作的特性之前要注意一下。

PartialOrd

在型別上加上#[derive(Educe)]#[educe(PartialOrd)]屬性,就可以替該型別實作PartialOrd特性。型別可以是任意結構體和任意列舉。此外,它還能夠忽略掉指定的欄位,也能夠替指定的欄位設定在比較是否相同時要使用的方法和(或)特性。另外,變體和欄位也都可以自訂排列順序。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(PartialEq, PartialOrd)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
忽略欄位

ignore屬性可以用來忽略指定的欄位。

#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(PartialEq, PartialOrd)]
enum Enum {
    V1,
    V2 {
        #[educe(PartialOrd(ignore))]
        f1: u8,
    },
    V3(
        #[educe(PartialOrd(ignore))]
        u8
    ),
}
使用另外的方法或特性來做比較

traitmethod參數可以被用在欄位上,取代該欄位的PartialOrd特性需求。如果您只有設定trait的參數的話,method參數會被自動預設為partial_cmp

#[macro_use]
extern crate educe;

use std::cmp::Ordering;

fn partial_cmp(a: &u8, b: &u8) -> Option<Ordering> {
    if a > b {
        Some(Ordering::Less)
    } else if a < b {
        Some(Ordering::Greater)
    } else {
        Some(Ordering::Equal)
    }
}

trait A {
    fn partial_cmp(&self, b: &Self) -> Option<Ordering>;
}

impl A for i32 {
    fn partial_cmp(&self, b: &i32) -> Option<Ordering> {
        if self > b {
            Some(Ordering::Less)
        } else if self < b {
            Some(Ordering::Greater)
        } else {
            Some(Ordering::Equal)
        }
    }
}

#[derive(Educe)]
#[educe(PartialEq, PartialOrd)]
enum Enum<T: std::cmp::PartialEq + A> {
    V1,
    V2 {
        #[educe(PartialOrd(method = "partial_cmp"))]
        f1: u8,
    },
    V3(
        #[educe(PartialOrd(trait = "A"))]
        T
    ),
}
泛型參數自動加上PartialOrd特性限制,或是手動設定其它限制

在型別上加上#[educe(PartialOrd(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作PartialOrd特性的條件下來替型別實作PartialOrd特性。

#[macro_use]
extern crate educe;

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

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

use std::cmp::Ordering;

trait A {
    fn partial_cmp(&self, b: &Self) -> Option<Ordering>;
}

impl A for i32 {
    fn partial_cmp(&self, b: &i32) -> Option<Ordering> {
        if self > b {
            Some(Ordering::Less)
        } else if self < b {
            Some(Ordering::Greater)
        } else {
            Some(Ordering::Equal)
        }
    }
}

#[derive(Educe)]
#[educe(PartialEq(bound), PartialOrd(bound = "T: std::cmp::PartialOrd, K: std::cmp::PartialOrd + A"))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(PartialOrd(trait = "A"))]
        f1: K,
    },
    V3(
        T
    ),
}
排列順序

每個欄位都可以加上#[educe(PartialOrd(rank = priority_value))]屬性,其中的priority_value是一個正整數,用來決定比較的優先順序(priority_value愈小就愈優先)。欄位的priority_value預設值與其被定義的序數(也就是第幾個欄位)有關,並且總是會小於自訂的priority_value值。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(PartialEq, PartialOrd)]
struct Struct {
    #[educe(PartialOrd(rank = 1))]
    f1: u8,
    #[educe(PartialOrd(rank = 0))]
    f2: u8,
}

每個變體都可以加上#[educe(PartialOrd(rank = comparison_value))]屬性,其中的comparison_value是一個正整數,用來覆寫變體代表的值或是序數的值,用於比較大小時。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(PartialEq, PartialOrd)]
enum Enum {
    #[educe(PartialOrd(rank = 2))]
    Two,
    #[educe(PartialOrd(rank = 1))]
    One,
}
Ord

在型別上加上#[derive(Educe)]#[educe(Ord)]屬性,就可以替該型別實作Ord特性。型別可以是任意結構體和任意列舉。此外,它還能夠忽略掉指定的欄位,也能夠替指定的欄位設定在比較是否相同時要使用的方法和(或)特性。另外,變體和欄位也都可以自訂排列順序。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(PartialEq, Eq, PartialOrd, Ord)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
忽略欄位

ignore屬性可以用來忽略指定的欄位。

#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(PartialEq, Eq, PartialOrd, Ord)]
enum Enum {
    V1,
    V2 {
        #[educe(Ord(ignore))]
        f1: u8,
    },
    V3(
        #[educe(Ord(ignore))]
        u8
    ),
}
使用另外的方法或特性來做比較

traitmethod參數可以被用在欄位上,取代該欄位的Ord特性需求。如果您只有設定trait的參數的話,method參數會被自動預設為cmp

#[macro_use]
extern crate educe;

use std::cmp::Ordering;

fn cmp(a: &u8, b: &u8) -> Ordering {
    if a > b {
        Ordering::Less
    } else if a < b {
        Ordering::Greater
    } else {
        Ordering::Equal
    }
}

trait A {
    fn cmp(&self, b: &Self) -> Ordering;
}

impl A for i32 {
    fn cmp(&self, b: &i32) -> Ordering {
        if self > b {
            Ordering::Less
        } else if self < b {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

#[derive(Educe)]
#[educe(PartialEq, Eq, PartialOrd, Ord)]
enum Enum<T: std::cmp::PartialOrd + A> {
    V1,
    V2 {
        #[educe(Ord(method = "cmp"))]
        f1: u8,
    },
    V3(
        #[educe(Ord(trait = "A"))]
        T
    ),
}
泛型參數自動加上Ord特性限制,或是手動設定其它限制

在型別上加上#[educe(Ord(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作Ord特性的條件下來替型別實作Ord特性。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(PartialEq(bound), Eq(bound), PartialOrd(bound), Ord(bound))]
enum Enum<T, K> {
    V1,
    V2 {
        f1: K,
    },
    V3(
        T
    ),
}

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

use std::cmp::Ordering;

trait A {
    fn cmp(&self, b: &Self) -> Ordering;
}

impl A for i32 {
    fn cmp(&self, b: &i32) -> Ordering {
        if self > b {
            Ordering::Less
        } else if self < b {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

#[derive(Educe)]
#[educe(PartialEq(bound), Eq(bound), PartialOrd(bound), Ord(bound = "T: std::cmp::Ord, K: std::cmp::Ord + A"))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(Ord(trait = "A"))]
        f1: K,
    },
    V3(
        T
    ),
}
排列順序

每個欄位都可以加上#[educe(Ord(rank = priority_value))]屬性,其中的priority_value是一個正整數,用來決定比較的優先順序(priority_value愈小就愈優先)。欄位的priority_value預設值與其被定義的序數(也就是第幾個欄位)有關,並且總是會小於自訂的priority_value值。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(PartialEq, Eq, PartialOrd, Ord)]
struct Struct {
    #[educe(Ord(rank = 1))]
    f1: u8,
    #[educe(Ord(rank = 0))]
    f2: u8,
}

每個變體都可以加上#[educe(Ord(rank = comparison_value))]屬性,其中的comparison_value是一個正整數,用來覆寫變體代表的值或是序數的值,用於比較大小時。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(PartialEq, Eq, PartialOrd, Ord)]
enum Enum {
    #[educe(Ord(rank = 2))]
    Two,
    #[educe(Ord(rank = 1))]
    One,
}
Hash

在型別上加上#[derive(Educe)]#[educe(Hash)]屬性,就可以替該型別實作Hash特性。型別可以是任意結構體和任意列舉。此外,它還能夠忽略掉指定的欄位,也能夠替指定的欄位設定雜湊時要使用的方法和(或)特性。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(Hash)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
忽略欄位

ignore屬性可以用來忽略指定的欄位。

#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(Hash)]
enum Enum {
    V1,
    V2 {
        #[educe(Hash(ignore))]
        f1: u8,
    },
    V3(
        #[educe(Hash(ignore))]
        u8
    ),
}
使用另外的方法或特性來做雜湊

traitmethod參數可以被用在欄位上,取代該欄位的Hash特性需求。如果您只有設定trait的參數的話,method參數會被自動預設為hash

#[macro_use]
extern crate educe;

use std::hash::{Hash, Hasher};

fn hash<H: Hasher>(_s: &u8, state: &mut H) {
    Hash::hash(&100, state)
}

trait A {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&100, state)
    }
}

impl A for i32 {};
impl A for u64 {};

#[derive(Educe)]
#[educe(Hash)]
enum Enum<T: A> {
    V1,
    V2 {
        #[educe(Hash(method = "hash"))]
        f1: u8,
    },
    V3(
        #[educe(Hash(trait = "A"))]
        T
    ),
}
泛型參數自動加上Hash特性限制,或是手動設定其它限制

在型別上加上#[educe(Hash(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作Hash特性的條件下來替型別實作Hash特性。

#[macro_use]
extern crate educe;

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

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

use std::hash::{Hash, Hasher};

fn hash<H: Hasher>(_s: &u8, state: &mut H) {
    Hash::hash(&100, state)
}

trait A {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&100, state)
    }
}

impl A for i32 {};
impl A for u64 {};

#[derive(Educe)]
#[educe(Hash(bound = "T: std::hash::Hash, K: A"))]
enum Enum<T, K> {
    V1,
    V2 {
        #[educe(Hash(trait = "A"))]
        f1: K,
    },
    V3(
        T
    ),
}
Default

在型別上加上#[derive(Educe)]#[educe(Default)]屬性,就可以替該型別實作Default特性。型別可以是任意結構體、任意列舉和任意聯合。此外,它還能直接指定整個型別的預設值,或是型別內特定欄位的預設值。

基本用法

對於列舉和聯合,您必須要再指派一個列舉的變體或是一個聯合的欄位來當作預設值,除非該列舉的變體數量或是聯合的欄位數量剛好只有一個。

#[macro_use]
extern crate educe;

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

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

#[derive(Educe)]
#[educe(Default)]
union Union {
    f1: u8,
    #[educe(Default)]
    f2: f64,
}
整個型別的預設值

替型別加上#[educe(Default(expression = "expression"))]屬性,可以用一個表達式來當作是這個型別的預設值。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(Default(expression = "Struct { f1: 1 }"))]
struct Struct {
    f1: u8
}

#[derive(Educe)]
#[educe(Default(expression = "Enum::Struct { f1: 1 }"))]
enum Enum {
    Unit,
    Struct {
        f1: u8
    },
    Tuple(u8),
}

#[derive(Educe)]
#[educe(Default(expression = "Union { f1: 1 }"))]
union Union {
    f1: u8,
    f2: f64,
}
指定欄位的預設值

也可以替欄位加上#[educe(Default = literal)]#[educe(Default(expression = "expression"))]屬性,可以直接指派一個定數值或是一個表達式作為該欄位的預設值。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(Default)]
struct Struct {
    #[educe(Default = 1)]
    f1: u8,
    #[educe(Default = 11111111111111111111111111111)]
    f2: i128,
    #[educe(Default = 1.1)]
    f3: f64,
    #[educe(Default = true)]
    f4: bool,
    #[educe(Default = "Hi")]
    f5: &'static str,
    #[educe(Default = "Hello")]
    f6: String,
    #[educe(Default = 'M')]
    f7: char,
}

#[derive(Educe)]
#[educe(Default)]
enum Enum {
    Unit,
    #[educe(Default)]
    Tuple(
        #[educe(Default(expression = "0 + 1"))]
        u8,
        #[educe(Default(expression = "-11111111111111111111111111111 * -1"))]
        i128,
        #[educe(Default(expression = "1.0 + 0.1"))]
        f64,
        #[educe(Default(expression = "!false"))]
        bool,
        #[educe(Default(expression = "\"Hi\""))]
        &'static str,
        #[educe(Default(expression = "String::from(\"Hello\")"))]
        String,
        #[educe(Default(expression = "'M'"))]
        char,
    ),
}

#[derive(Educe)]
#[educe(Default)]
union Union {
    f1: u8,
    f2: i128,
    f3: f64,
    f4: bool,
    #[educe(Default = "Hi")]
    f5: &'static str,
    f6: char,
}
泛型參數自動加上Default特性限制,或是手動設定其它限制

在型別上加上#[educe(Default(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作Default特性的條件下來替型別實作Default特性。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(Default(bound))]
enum Enum<T> {
    Unit,
    #[educe(Default)]
    Struct {
        f1: T
    },
    Tuple(T),
}

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(Default(bound = "T: std::default::Default"))]
enum Enum<T> {
    Unit,
    #[educe(Default)]
    Struct {
        f1: T
    },
    Tuple(T),
}
new關聯函數

替型別加上#[educe(Default(new))]屬性,可以使它擁有一個new關聯函數。這個new關聯函數會去調用Default特性的default關聯函數。

#[macro_use]
extern crate educe;

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

在型別上加上#[derive(Educe)]#[educe(Clone)]屬性,就可以替該型別實作Clone特性。型別可以是任意結構體、任意列舉和任意聯合。此外,它還能夠替指定的欄位設定在複製時要使用的方法和(或)特性。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(Clone)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
使用另外的方法或特性來做複製

traitmethod參數可以被用在欄位上,取代該欄位的Clone特性需求。如果您只有設定trait的參數的話,method參數會被自動預設為clone

#[macro_use]
extern crate educe;

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

trait A {
    fn clone(&self) -> Self;
}

impl A for i32 {
    fn clone(&self) -> i32 {
        self + 100
    }
}

impl A for u64 {
    fn clone(&self) -> u64 {
        self + 100
    }
}

#[derive(Educe)]
#[educe(Clone)]
enum Enum<T: A> {
    V1,
    V2 {
        #[educe(Clone(method = "clone"))]
        f1: u8,
    },
    V3(
        #[educe(Clone(trait = "A"))]
        T
    ),
}
泛型參數自動加上Clone特性限制,或是手動設定其它限制

在型別上加上#[educe(Clone(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作Clone特性或是Copy特性的條件下(後者會發生在有使用#[educe(Copy)]屬性時)來替型別實作Clone特性。

#[macro_use]
extern crate educe;

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

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

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

trait A {
    fn clone(&self) -> Self;
}

impl A for i32 {
    fn clone(&self) -> i32 {
        self + 100
    }
}

impl A for u64 {
    fn clone(&self) -> u64 {
        self + 100
    }
}

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

#[educe(Clone)]屬性可以被用在聯合上,但是有個前提,就是這個聯合必須要實作Copy特性。聯合中的欄位無法用其它的特性或是方法來複製。

#[macro_use]
extern crate educe;

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

在型別上加上#[derive(Educe)]#[educe(Copy)]屬性,就可以替該型別實作Copy特性。型別可以是任意結構體、任意列舉和任意聯合。

基本用法
#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(Copy, Clone)]
enum Enum {
    V1,
    V2 {
        f1: u8,
    },
    V3(u8),
}
泛型參數自動加上Copy特性限制,或是手動設定其它限制

在型別上加上#[educe(Copy(bound))]屬性,會在該型別用到的泛型型別參數有被限制為必須實作Copy特性的條件下來替型別實作Copy特性。

#[macro_use]
extern crate educe;

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

或者,您也可以自行撰寫where語法後的限定條件,如下:

#[macro_use]
extern crate educe;

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

trait A {
    fn clone(&self) -> Self;
}

impl A for i32 {
    fn clone(&self) -> i32 {
        self + 100
    }
}

impl A for u64 {
    fn clone(&self) -> u64 {
        self + 100
    }
}

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

如果您用Educe來同時實作CopyClone特性,且並沒有手動指定欄位要用來做複製的方法或是特性的話,則Clone特性的限制條件(bound)中必須有Copy特性,這是為了要實現Copy, Clone的效能優化。

Deref

在型別上加上#[derive(Educe)]#[educe(Deref)]屬性,就可以替該型別實作Deref特性。型別可以是任意結構體和任意列舉。

基本用法

您需要指定一個欄位作為預設取得的不可變參考的欄位,除非該結構體或是列舉的變體的欄位數量剛好只有一個。

#[macro_use]
extern crate educe;

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

#[derive(Educe)]
#[educe(Deref)]
enum Enum {
    Struct {
        f1: u8
    },
    Struct2 {
        f1: u8,
        #[educe(Deref)]
        f2: u8,
    },
    Tuple(u8),
    Tuple2(
        u8,
        #[educe(Deref)]
        u8
    ),
}
DerefMut

在型別上加上#[derive(Educe)]#[educe(DerefMut)]屬性,就可以替該型別實作DerefMut特性。型別可以是任意結構體和任意列舉。

基本用法

您需要指定一個欄位作為預設取得的可變參考的欄位,除非該結構體或是列舉的變體的欄位數量剛好只有一個。

#[macro_use]
extern crate educe;

#[derive(Educe)]
#[educe(Deref, DerefMut)]
struct Struct {
    f1: u8,
    #[educe(Deref, DerefMut)]
    f2: u8,
}

#[derive(Educe)]
#[educe(Deref, DerefMut)]
enum Enum {
    Struct {
        f1: u8
    },
    Struct2 {
        f1: u8,
        #[educe(Deref, DerefMut)]
        f2: u8,
    },
    Tuple(u8),
    Tuple2(
        #[educe(DerefMut)]
        u8,
        #[educe(Deref)]
        u8
    ),
}

不可變參考欄位和可變參考欄位不一定要一樣,但是它們的型別必須要是相同的。