Rust程式語言內建的derive
屬性可以快速地替我們的型別加上內建的特性,不過會要求該型別內的欄位也都要先實作相同的特性,而且很多時候利用derive
屬性實作出來的特性並不能達到我們期望的功能。
好比說以下這個用來作為唯讀資料庫的資料模型的結構體:
#[derive(Hash, PartialEq, Eq)]
pub struct Person {
uid: u64,
name: String,
age: u8
}
我們希望這個Person
結構體可以被雜湊,且用有判斷兩個Person
結構體是否相同的功能。以上的實作方式雖然在使用起來並沒有什麼太大的問題,但是效能不會很好,因為透過derive屬性實作的Hash
特性,會去計算所有欄位的雜湊值,然而在這裡我們其實只要去計算uid
這個欄位的雜湊值就可以了。同理,PartialEq
特性也是一樣只需要去判斷uid
這個欄位是否PartialEq
就好了,實在不需要用上所有欄位。
再來,如果我們加了某個沒有同時實作Hash
、PartialEq
和Eq
特性的欄位,這個Person
結構體就沒有辦法被編譯了。
例如:
#[derive(Hash, PartialEq, Eq)]
pub struct Person {
uid: u64,
name: String,
age: u8,
score: f64,
}
以上程式,由於型別為f64
的score
欄位沒有實作Hash
和Eq
特性,所以會編譯失敗。
還有,如果我們的型別欄位有用到泛型的話,泛型型別參數也必須要加上特性的限制,否則也會無法編譯。
例如:
#[derive(Hash, PartialEq, Eq)]
pub struct Person<T> {
uid: u64,
name: T,
age: u8,
}
若以上這個Person
結構體要成功使用derive屬性來實作Hash
、PartialEq
和Eq
特性的話,要改成以下這樣:
use std::hash::Hash;
#[derive(Hash, PartialEq, Eq)]
pub struct Person<T: Hash + PartialEq + Eq> {
uid: u64,
name: T,
age: u8,
}
然而做這樣的修改可能會使這個結構體想要表現的功能和原本的不同。原本我們可能預期泛型型別參數T
就是一個任意的型別,不一定要實作Hash
、PartialEq
和Eq
特性,然而將其加上特性的限制之後,T
就不能是任意的型別了。為了解決這個問題,我們必須要手動實作特性,並且只在實作時替泛型型別參數加上特性的限制,而不是像以上這樣直接在型別上進行限制。
Educe
「Educe」是筆者開發的程序式巨集套件,可以用來加強原先Rust程式語言用derive
屬性來實作內建特性的方式,使得這類程式序巨集可以在更多常見的場景下直接使用,讓開發者不需要手動去寫impl
相關的程式。
Crates.io
Cargo.toml
使用方法
Educe目前能夠實作的特性有Debug
、PartialEq
、Eq
、PartialOrd
、Ord
、Hash
、Default
、Clone
、Copy
、Deref
、DerefMut
。
上面的泛型型別參數例子可以很輕易地用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
),
}
使用另外的方法或特性來做格式化
trait
和method
參數可以被用在欄位上,取代該欄位的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
),
}
使用另外的方法或特性來做比較
trait
和method
參數可以被用在欄位上,取代該欄位的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
),
}
使用另外的方法或特性來做比較
trait
和method
參數可以被用在欄位上,取代該欄位的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
),
}
使用另外的方法或特性來做比較
trait
和method
參數可以被用在欄位上,取代該欄位的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
),
}
使用另外的方法或特性來做雜湊
trait
和method
參數可以被用在欄位上,取代該欄位的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),
}
使用另外的方法或特性來做複製
trait
和method
參數可以被用在欄位上,取代該欄位的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來同時實作Copy
和Clone
特性,且並沒有手動指定欄位要用來做複製的方法或是特性的話,則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
),
}
不可變參考欄位和可變參考欄位不一定要一樣,但是它們的型別必須要是相同的。