用Rust的derive属性实作内置特性(如:Copy、Clone、Debug、Default、Hash、PartialEq和Eq等特性)的替代方式

3,810 阅读21分钟

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

crates.io/crates/educ…

Cargo.toml

educe = "0.4.20"

使用方法

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(Clone(trait = "A"))]
        f1: K,
    },
    V3(
        T
    ),
}
复制代码
Copy 和 Clone

如果您用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
    ),
}
复制代码

不可变参考字段和可变参考字段不一定要一样,但是它们的类型必须要是相同的。

原文转自:magiclen.org/educe/

关于作者 Magic Len

Magic Len,台湾台中大肚山上人,毕业于台中高工信息科和台湾科技大学信息工程系,曾在桃机航警局服役。热爱自然也热爱科学,喜欢和别人分享自己的知识与经验。如果有兴趣认识作者,可以加Facebook(点我),并且请注明是从MagicLen来的。