給Rust的Struct自動(dòng)實(shí)現(xiàn)trait
我們通常使用
#[derive(Clone,?Debug)]
這樣的方式給struct自動(dòng)實(shí)現(xiàn)相應(yīng)的trait,從而讓struct具備某些特性,但是如果我們想讓編譯器給struct自動(dòng)實(shí)現(xiàn)自己定義的trait要怎么辦?
首先我們需要有一個(gè)trait,假設(shè)如下面的定義:
pub?trait?Printable?{
????pub?fn?print_me(&self);
}
我們定義這個(gè)trait給struct賦予一個(gè)行為是逐行打印struct的所有Field。當(dāng)然如果是自己實(shí)現(xiàn)肯定是可以憑空亂寫的,那么我們可以和Debug一樣,在 derive 中讓編譯器自動(dòng)添加默認(rèn)的實(shí)現(xiàn)。
首先需要給crate添加一個(gè)子crate:
cargo?new?--lib?printable
然后在當(dāng)前crate的 Cargo.toml 中添加依賴
[workspace]
members?=?[
????".",
????"printable"
]
[dependencies]
printable?=?{?version?=?"*",?path?=?"printable"}
在printable 的 Cargo.toml 里還需要添加依賴
[dependencies]
syn?=?{?version?=?"1.0",?features?=?["full"]?}
quote?=?"1.0"
proc-macro2?=?"1.0.51"
我們需要這三個(gè)crate來簡化代碼生成的工作,這里proc-macro2提供了自動(dòng)實(shí)現(xiàn)宏的功能,syn用來解析結(jié)構(gòu)體,quote用來輸出TokenStream。
在 printable 的lib.rs 文件中
#[proc_macro_derive(Printable)]
pub?fn?print_info_derive(input:?TokenStream)?->?TokenStream?{
}
我們?cè)诤瘮?shù) print_info_derive 中輸出的TokenStream,就會(huì)在編譯時(shí)動(dòng)態(tài)注入到struct中,這里參數(shù)input就是struct本身的代碼流。
我們通過解析input就可以分析出 Struct的名字,F(xiàn)ield列表,所有Field的名字,類型…..
下面是簡化后的代碼:
#[proc_macro_derive(Printable)]
pub?fn?print_info_derive(input:?TokenStream)?->?TokenStream?{
????let?struct_name?=?to_snake_case(input.ident.to_string().as_str());
????????
????let?fields?=?match?input.data.clone()?{
????????syn::Data::Struct(data)?=>?data.fields,
????????_?=>?panic!("Only?structs?are?supported"),
????};
????let?fields_name:?Vec<Ident>?=?fields.iter().map(|field|?{
????????field.ident.as_ref().unwrap().clone()
????}).collect();
}
之后我們就需要構(gòu)建輸出的代碼流,這里使用 quote!
這個(gè)宏來實(shí)現(xiàn)。
#[proc_macro_derive(Printable)]
pub?fn?print_info_derive(input:?TokenStream)?->?TokenStream?{
????let?struct_name?=?to_snake_case(input.ident.to_string().as_str());
????????
????let?fields?=?match?input.data.clone()?{
????????syn::Data::Struct(data)?=>?data.fields,
????????_?=>?panic!("Only?structs?are?supported"),
????};
????let?fields_name:?Vec<Ident>?=?fields.iter().map(|field|?{
????????field.ident.as_ref().unwrap().clone()
????}).collect();
????let?output_token?=?quote!?{
????????impl?Printable?for?#struct_name?{
????????????pub?fn?print_me(&self)?{
????????????????//這里添加逐行打印Field的代碼,因?yàn)閝uote里本來就是在輸出代碼流
????????????????//所以不能直接訪問fields_name,比如循環(huán)之類的,所以我們這里需要
????????????????//把生成這部分代碼提取到函數(shù)外
????????????}
????????}
????}
????output_token.into()
}
為了簡單演示我們就使用一個(gè)函數(shù)來實(shí)現(xiàn):
fn?gen_print(fileds:?Vec<Ident>)?->?TokenStream2?{
????let?print_stmts?=fields.iter().map(|field|?{
????????quote!?{
????????????println!("field:{}",?&self.#field);
????????}
????});
????quote!{
????????#(#print_stmts)*
????}
}
最后組裝一下,lib.rs ?的代碼如下:
#[proc_macro_derive(Printable)]
pub?fn?print_info_derive(input:?TokenStream)?->?TokenStream?{
????let?struct_name?=?to_snake_case(input.ident.to_string().as_str());
????????
????let?fields?=?match?input.data.clone()?{
????????syn::Data::Struct(data)?=>?data.fields,
????????_?=>?panic!("Only?structs?are?supported"),
????};
????let?fields_name:?Vec<Ident>?=?fields.iter().map(|field|?{
????????field.ident.as_ref().unwrap().clone()
????}).collect();
????let?print_code?=?gen_print(fields_name);
????let?output_token?=?quote!?{
????????impl?Printable?for?#struct_name?{
????????????pub?fn?print_me(&self)?{
????????????????#output_token
????????????}
????????}
????}
????output_token.into()
}
如此這般一通操作后,我們隨便一定一個(gè)Struct:
#[derive(Debug,?Printable)]
struct?TestTb?{
????id:?String,
????name:?String,
????ts:?i32
}
就可以
TestTb{id:?"123".to_string(),?name:?"alex",?ts:?111}.print_me();
就可以逐行打印出所有的Field了。
那么靈活的使用這個(gè)玩法,我們可以根據(jù)Struct的Field,自動(dòng)生成 insert, update, delete的SQL也是可以的。給每個(gè)Field自動(dòng)生成getter,setter方法…… (這個(gè)Java味太濃了,だめ)
研究這個(gè)是為了給 sqlx 增加一個(gè)自動(dòng)生成insert,update,delete方法的增強(qiáng),因?yàn)椴幌矚g寫超長的insert和update語句。文章來源:http://www.zghlxwxcb.cn/news/detail-828716.html
Amusez-vous tous!文章來源地址http://www.zghlxwxcb.cn/news/detail-828716.html
到了這里,關(guān)于【社區(qū)投稿】給Rust的Struct自動(dòng)實(shí)現(xiàn)trait的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!