ã¯ããã« Rust ã®ãã¯ã 宣èšçãã¯ã æç¶ãçãã¯ã proc_macro_workshop proc_macro_workshop ã«ã€ã㊠proc_macro_workshop ã®é²ãæ¹ derive(Builder) ãã¯ããäœã ãã¯ãã®åŠçã®æµã ãã¯ããéçºããäžã§ã® Tips dbg!ãã¯ãã®äœ¿çš cargo-expand 01-parse ç®æš å®è£
æ¹é å®è£
02-create-builder ç®æš å®è£
æ¹é å®è£
空ã®builder颿°ãCommandæ§é äœã«å®è£
ãã CommandBuilderãè¿ãããã«builder颿°ã®å®è£
ã倿Žãã æ§é äœåããã£ãŒã«ãåã«å¿ãã Builder æ§é äœãçæãã 03-call-setters ç®æš å®è£
æ¹é å®è£
04-call-build ç®æš å®è£
æ¹é å®è£
05-method-chaining ç®æš å®è£
æ¹é å®è£
ãŸãšã åèæç® ã¯ããã« ãã®èšäºã§ã¯ proc_macro_workshop ãšãããªããžããªã䜿ã£ãŠ Rust ã®æç¶ãçãã¯ãã®äœãæ¹ãåŠãã§ãããŸããæ³å®ããŠããèªè
ã¯ä»¥äžã®ãããªæ¹ã§ãã Rust ã®åºæ¬çãªææ³ãæŠå¿µïŒãã¬ã€ããæææš©ãã©ã€ãã¿ã€ã ãªã©ïŒãç¥ã£ãŠãã æç¶ãçãã¯ãã®äœãæ¹ã«ã€ããŠç¥ããã ãã®èšäºã§ã¯ä»¥äžã®ããšã説æããŸãã Rust ã®ãã¯ãã®æŠèŠ æç¶ãçãã¯ãïŒ derive ãã¯ãïŒã®äœãæ¹ proc_macro_workshop ã®é²ãæ¹ ãŸãããã®èšäºã§ã¯ä»¥äžã®ããšã¯èª¬æ ããŸãã ã Rust ã®åºæ¬çãªææ³ãæŠå¿µ 宣èšçãã¯ãã®äœãæ¹ Rust ã®ãã¯ã Rust ã®ãã¯ãã«ã¯å®£èšçãã¯ããšæç¶ãçãã¯ãã® 2 çš®é¡ããããŸãã 宣èšçãã¯ã 宣èšçãã¯ã㯠macro_rules! æ§æã䜿çšããŠå®çŸ©ããããã¯ãã§ãmatch åŒã«äŒŒã圢ã§åŠçå
容ãå®çŸ©ããŸãã vec! ã println! ãªã©æ®æ®µè¯ã䜿ããã¯ãã宣èšçãã¯ãã§ãã 宣èšçãã¯ãã«ã€ããŠã¯ The Rust Programming Language æ¥æ¬èªç ã« vec! ãã¯ããäŸã«ãã解説ãããã®ã§ããã¡ããã芧ãã ããã æç¶ãçãã¯ã æç¶ãçãã¯ãã¯ãã¬ã€ãã®ããã©ã«ãå®è£
ã®å°åºã«çšãã derive ãã¯ããªã©ã«ä»£è¡šããããã¯ãã§ãã宣èšçãã¯ããããè€éãªåŠçãèšè¿°ã§ããããšãç¹åŸŽã§ã以äžã® 3 çš®é¡ããããŸãã derive ãã¯ã æ§é äœãenumã«ä»äžããããšã§ãã®æ§é äœãenumã«è¿œå ã®åŠçãå®è£
ã§ããŸã 颿°é¢šãã¯ã 颿°é¢šãã¯ãã¯å®£èšçãã¯ãã®ããã«é¢æ°åŒã³åºããšäŒŒã圢ã§äœ¿çšã§ãããã¯ãã§ãã宣èšçãã¯ããšæ¯èŒãããšããè€éãªåŠçãèšè¿°ã§ããŸã 屿§é¢šãã¯ã deriveãã¯ããšåæ§ã«ä»äžãã察象ã«å¯ŸããŠè¿œå ã®åŠçãå®è£
ã§ããŸããããã¡ãã¯æ§é äœãenumã ãã§ãªã颿°ãªã©ã«ãé©çšã§ããŸãããã¹ããæžããšãã«çšãã #[test] ã¢ããªãã¥ãŒããªã©ãããã«ããããŸã æç¶ãçãã¯ãã«ã€ããŠã¯ The Rust Programming Language æ¥æ¬èªç ã«ãã詳ãã説æãããã®ã§ãããããã ãã§å®éã«äœ¿ãããããªæç¶ãçãã¯ããäœãå§ããã®ã¯é£ããããšæããŸãããã®èšäºã§ã¯ proc_macro_workshop ãšãããªããžããªã䜿ã£ãŠå®éã«æãåãããªããæç¶çãã¯ãã®äœãæ¹ãåŠãã§ãããŸãã proc_macro_workshop proc_macro_workshop ã«ã€ã㊠proc_macro_workshop ã«ã¯äœæãããã¯ãã®çš®é¡ã«ãã£ãŠ 5 ã€ã®ãããžã§ã¯ãããããŸãã derive ãã¯ã derive(Builder) derive ãã¯ã derive(CustomDebug) 颿°é¢šãã¯ã seq! 屿§é¢šãã¯ã #[sorted] 屿§é¢šãã¯ã #[bitfield] ãããã®ãã¯ãã®å
容ã«ã€ããŠã¯ proc_macro_workshop ãªããžã㪠ãã芧ãã ããã ãã®èšäºã§ã¯ãBuilder ãã¿ãŒã³ã«å¿
èŠãªå®è£
ãå°åºãã derive(Builder) ãããžã§ã¯ããé²ãã€ã€æç¶ãçãã¯ãã®äœææ¹æ³ãåŠãã§ãããŸãã derive(Builder) ãããžã§ã¯ããé²ãããšæçµçã«ã¯ä»¥äžã®ãããªäœ¿ãæ¹ã®ã§ãããã¯ãã宿ããŸãã use derive_builder :: Builder; #[derive(Builder)] pub struct Command { executable: String , #[builder(each = "arg" )] args: Vec < String > , #[builder(each = "env" )] env: Vec < String > , current_dir: Option < String > , } fn main () { let command = Command :: builder () . executable ( "cargo" . to_owned ()) . arg ( "build" . to_owned ()) . arg ( "--release" . to_owned ()) . build () . unwrap (); } proc_macro_workshop ã®é²ãæ¹ proc_macro_workshop ã®åãããžã§ã¯ãã¯ä»¥äžã®æµãã§é²ããŠãããŸãã ãã¹ãã±ãŒã¹ã远å ãã tests/progress.rs ã«èšèŒãããŠãããã¹ãã±ãŒã¹ã®ã³ã¡ã³ãã¢ãŠããè§£é€ãã 远å ãããã¹ãã±ãŒã¹ããã¹ãããããªãã¯ããå®è£
ãã src/lib.rs ã«å®è£
ããŸã ãã¹ãããã¹ããã 1.ã«æ»ã proc_macro_workshop ã®åãããžã§ã¯ãã«ã¯ããããã®ãã¯ãããã¹ãã¹ããã¹ããèšèŒãããŠããããããã®ãã¹ãã«ééãããããªãã¯ããå®è£
ããããšãç®æšãšãªããŸãã derive(Builder) ãããžã§ã¯ãã®å Žå㯠builder/tests ãã£ã¬ã¯ããªã«ãã¹ãã±ãŒã¹ãèšèŒããããã¡ã€ã«ãæ ŒçŽãããŠããŸããåã¹ãããã§ãã¹ãã±ãŒã¹ã 1 ã€ãã€å¢ãããŠãããããã«å¯Ÿå¿ããæ©èœãå®è£
ããŠããŸãã ãããžã§ã¯ãã®é²æã¯ tests/progress.rs ãçšããŠç®¡çããŸãã tests/progress.rs ã¯ä»¥äžã®ããã«ãªã£ãŠãããåè¡ãããããã®ãã¹ãã±ãŒã¹ã«å¯Ÿå¿ããŠããŸããåã¹ãããã§ãã¹ãã±ãŒã¹ã®ã³ã¡ã³ãã¢ãŠããè§£é€ããããããã®ãã¹ããééããããã«å®è£
ãé²ããŠãããŸãã #[test] fn tests () { let t = trybuild :: TestCases :: new (); t. pass ( "tests/01-parse.rs" ); //t.pass("tests/02-create-builder.rs"); //t.pass("tests/03-call-setters.rs"); //t.pass("tests/04-call-build.rs"); //t.pass("tests/05-method-chaining.rs"); //t.pass("tests/06-optional-field.rs"); //t.pass("tests/07-repeated-field.rs"); //t.compile_fail("tests/08-unrecognized-attribute.rs"); //t.pass("tests/09-redefined-prelude-types.rs"); } åãã¹ãã±ãŒã¹ã«ãããç®æšã¯ãã¹ãã±ãŒã¹ãèšèŒãããŠãããã¡ã€ã«ã®ã³ã¡ã³ãã«æžããŠãããŸããå®éã«äœæ¥ããéã¯ãããã®ã³ã¡ã³ããåèã«ãã€ã€å®è£
ããŠããããšã«ãªããŸãã ãã¹ã㯠builder ãã£ã¬ã¯ããªã§ cargo test ã³ãã³ããå®è¡ããããšã§å®è¡ã§ããŸãã derive(Builder) ãã¯ããäœã derive(Builder) ãããžã§ã¯ãã§ã¯å€§ãŸãã«ä»¥äžã®ãããªæµãã§ãã¯ããäœæããŠãããŸãã ãã¯ãã®ã²ãªåãçšæãã 01-parse ã§äœæ¥ããŸã builder ãã¿ãŒã³ãå®çŸããããã«å¿
èŠãªæ©èœïŒsetter ã¡ãœãããªã©ïŒãå®è£
ãã 02-create-builder ~ 05-method-chaining ã§äœæ¥ããŸã ãšã©ãŒãã³ããªã³ã°ãªã©ãå®è£
ãã 06-optional-field ~ 09-redefined-prelude-types ã§äœæ¥ããŸã ããã§ã¯å®éã«ãã¯ããæžããŠãããŸãããã以éã®åç¯ã®ååã¯ãã¹ããã¡ã€ã«ã®åç§°ãšå¯Ÿå¿ããŠããŸãã以éã®åç¯ã§ã¯ä»¥äžã®æµãã§é²ããŠãããŸãã ã¹ãããã®ç®æšã確èªãã å®éã«ã¯å
·äœçãªãã¹ãã±ãŒã¹ããããŸããããã¹ãŠèšèŒãããšç
©éãªã®ã§åã¹ãããã®ãŽãŒã«ã®ã¿èšèŒããŸã å®è£
æ¹éã®èª¬æ å®è£
åã¹ãããã®æåŸã«ãã¹ãã±ãŒã¹ããã¹ããå®è£
äŸãæç€ºããŸã ãã¯ãã®åŠçã®æµã Rust ã®æç¶ãåãã¯ãã®åŠçã¯åºæ¬çã«ä»¥äžã®ããã«ãªã£ãŠããŸããæ¬èšäºã§ã¯ãã®å
容ãå
·äœçã«å®è£
ããŠãããŸãã ããŒã¯ã³ åãå
¥åãšããŠåãåã åãåã£ãããŒã¯ã³åãæ§ææšã«å€æãã 倿ããæ§ææšãããšã«åŠçãè¡ãææã®æ§ææšãåŸã åŸãããæ§ææšãããŒã¯ã³åã«å€æããŠè¿ã ããŒã¯ã³åãšæ§ææšã®çžäºå€æã«ã¯ syn ã¯ã¬ãŒã ãš quote ã¯ã¬ãŒã ããã䜿ãããŸãããã®èšäºã§ããããã®ã¯ã¬ãŒããçšããŠãã¯ããå®è£
ããŸãã syn ã¯ã¬ãŒãã¯ããŒã¯ã³åããæ§ææšãžã®å€æã«äœ¿çšããŸãããŸãã syn ã¯ã¬ãŒãã«ã¯æ§ææšã«é¢ããæ§é äœãå®çŸ©ãããŠãããããŒã¹ããŠåŸãããæ§ææšã¯ãããã®æ§é äœã®ã€ã³ã¹ã¿ã³ã¹ãšããŠä¿æãããŸãã quote ã¯ã¬ãŒãã¯æ§ææšããããŒã¯ã³åãžã®å€æã«äœ¿çšããŸãã ãã¯ããéçºããäžã§ã® Tips dbg! ãã¯ãã®äœ¿çš ãã¯ãã®ãããã°ãããéãªã©ã«ã¯æ§ææšã®äžèº«ãå®éã«ã©ããªã£ãŠããããç¥ããããªãããšããããããããŸããã syn ã¯ã¬ãŒãã® extra-traits ãã£ãŒãã£ãŒ ãæå¹ã«ãããšãããã®æ§é äœã« Debug ãã¬ã€ããå®è£
ãã㊠dbg! ãã¯ãã䜿ã£ããããã°ãã§ããŸããéçºããäžã§äŸ¿å©ãªã®ã§éçºäžã¯æå¹ã«ããŠãããšè¯ãã§ãããã syn = { version = "1.0.86" , features = [ "extra-traits" ] } cargo-expand cargo expand ã䜿ããšãã¯ãã«ãã£ãŠçæãããã³ãŒããåºåã§ããŸããããããããã°ã«äŸ¿å©ãªã®ã§è©°ãŸã£ãæã¯äœ¿ããšè¯ãã§ãããã proc-macro-workshop ã® GitHub ãªããžã㪠㚠cargo-expand ã® GitHub ãªããžã㪠ã«äœ¿ãæ¹ã®èª¬æãããã®ã§ã䜿ãå Žåã¯ãã¡ããåç
§ããŠãã ããã 01-parse ç®æš 空㮠derive ãã¯ããäœã å®è£
æ¹é ãã¹ããå®è¡ãããã®ã¯ tests/01-parse.rs ã® main 颿°ã§ããããã®ã¹ãããã§ã¯äœãèšèŒããããŸãããã§ãã®ã§ãç¹ã«äœãããªããŠããã¹ãããã¹ããâŠããšãããšããã§ã¯ãªãã #[derive(Builder)] ã䜿ãããéã«åŒã°ãã derive ãã¯ããååšããŠããå¿
èŠããããŸãã ãªãããšãããšããã¹ãã³ãŒãäžã§ä»¥äžã®ããã«æ§é äœã«å¯Ÿã㊠derive ãã¯ããåŒã°ããŠããããã§ãã #[derive(Builder)] pub struct Command { executable: String , args: Vec < String > , env: Vec < String > , current_dir: String , } ãã¯ãã®åºæ¬çãªåŠçå
容ã«ã€ããŠã¯ãã§ã«èª¬æããŸããããããå
·äœçã«ã¯ä»¥äžã®ãããªæµãã«ãªã£ãŠããŸãã ããŒã¯ã³å proc_macro::TokenStream ïŒä»¥éã§ã¯åºæ¬çã« TokenStream ãšèšèŒããŸãïŒãåŒæ°ãšããŠåãåã syn::parse_macro_input! ãã¯ãã§ããŒã¹ããŠæ§ææšã«ãã ããŒã¹ããæ§ææšãå
ã«ææã®æ§ææšãçæãã çæããæ§ææšã TokenStream ã«å€æããŠè¿ã ãã®ã¹ãããã§ã¯ãã¯ããååšããŠããã°ããã®ã§ã1.ãš 4.ã®åŠçãå®è£
ããã°ãã¹ãããã¹ããŸãã å®è£
ããã§ã¯å®éã«å®è£
ãããŠãããŸãã ãŸã㯠derive ãã¯ãã®å®çŸ©ããå§ããŸãã颿°ã« proc_macro_derive ã¢ããªãã¥ãŒããã€ããããšã§ãã®é¢æ°å
ã«èšèŒãããåŠçã derive ãã¯ãã®å®è£
ã«ãªããŸãã derive ãã¯ãã®ã·ã°ããã£ã¯ (TokenStream) -> TokenStream ã«ãªã£ãŠããã宿
㯠Rust ã®ããŒã¯ã³ã TokenStream ãšããæ§é äœã§åãåããå
éšã§åŠçããåŸã« TokenStream ãšããæ§é äœãšããŠè¿ã颿°ã§ãã #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream {} ã§ã¯é¢æ°ã®äžèº«ãå®è£
ããŠããŸããããæäœéãã¹ããéãããã«ã¯ãã®é¢æ°ãã TokenStream ãè¿ãã°è¯ãã®ã§ã以äžã®ããã« TokenStream ãæ°ããäœã£ãŠè¿ããŠããã° OK ã§ãã #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { proc_macro :: TokenStream :: new () } ãã®ãŸãŸã§ãè¯ãã®ã§ãããæ¬¡ã®ã¹ããããžã®æºåãšããŠå
¥åãããŒã¹ã§ããããã«ãããŠãããŸããããå
¥åã®ããŒã¹ã«ã¯ syn::parse_macro_input! ãã¯ãã䜿ããŸããderive ãã¯ãã®å
¥å㯠syn::DeriveInput ã§å®çŸ©ãããæ§é ãããŠããã®ã§ DeriveInput ãšããŠããŒã¹ããŸããå
¥åãããŒã¹ããåŠçã远å ãããšæçµçãªãã¯ãã®å®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro :: TokenStream; use syn :: {parse_macro_input, DeriveInput}; #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let _input = parse_macro_input! (input as DeriveInput); proc_macro :: TokenStream :: new () } 02-create-builder ç®æš CommandBuilder æ§é äœã®ã€ã³ã¹ã¿ã³ã¹ãè¿ã builder 颿°ã Command æ§é äœã«å®è£
ãã å®è£
æ¹é æçµçã«ã¯ä»¥äžã®ãã㪠CommandBuilder æ§é äœã®ã€ã³ã¹ã¿ã³ã¹ãè¿ã builder 颿°ã Command æ§é äœã«å®è£
ããã®ãç®çã§ãã pub struct CommandBuilder { executable: Option < String > , args: Option < Vec < String >> , env: Option < Vec < String >> , current_dir: Option < Vec < String >> , } å¿
èŠãªäœæ¥ã¯å€§ããåããŠä»¥äžã® 2 ã€ã§ãã builder 颿°ã Command æ§é äœã«å®è£
ãã CommandBuilder æ§é äœã®å®çŸ©ãå®è£
ãã æ±çšçã«äœ¿ããããã«ããã«ã¯ Builder æ§é äœã®æ§é äœåããã£ãŒã«ãã®ååãå㯠derive ãã¯ããé©çšãããæ§é äœã«å¿ãããã®ã«ããå¿
èŠããã ãããåæã«å
šéšé²ããã®ã¯ãããžãã§ãã®ã§ã以äžã®é ã«é²ããŠãããŸãã 空㮠builder 颿°ã Command æ§é äœã«å®è£
ãã CommandBuilder ãè¿ãããã« builder 颿°ã®å®è£
ã倿Žãã æ§é äœåããã£ãŒã«ãåã«å¿ãã Builder æ§é äœãçæãã å®è£
空㮠builder 颿°ã Command æ§é äœã«å®è£
ãã åã®ã¹ãããã§èª¬æããããã«ãæç¶ãçãã¯ãã®åŠçã®æŠèŠã¯ä»¥äžã®éãã§ãã1.ãš 2.ã¯åã®ã¹ãããã§å®è£
ããŸããããã®ã¹ãããã§ã¯ 3.ãš 4.ã®åŠçãå®è£
ããŠãããŸãã ãŸãã¯ç©ºã® builder 颿°ãå®è£
ããåŠçãæžããŠãããŸãããããã®æ®µéã§ã¯å
¥åã«ã€ããŠæ°ã«ããå¿
èŠã¯ãããŸããã proc_macro::TokenStream ïŒä»¥éã§ã¯åºæ¬çã« TokenStream ãšèšèŒããŸãïŒãåŒæ°ãšããŠåãåã syn::parse_macro_input! ãã¯ãã§ããŒã¹ããŠæ§ææšã«ãã ããŒã¹ããæ§ææšãå
ã«ææã®æ§ææšãçæãã çæããæ§ææšã TokenStream ã«å€æããŠè¿ã ããŒã¯ã³åãçæããããã«ã¯ quote ã¯ã¬ãŒãã® quote! ãã¯ãã䜿ããŸãã以äžã®ããã« Rust ã®ã³ãŒããšïŒã»ãŒïŒåãæ§æã§èšè¿°ã§ãããã®å
容ãããŒã¯ã³åã«å€æãããŸãã #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let _input = parse_macro_input! (input as DeriveInput); let expand = quote! { impl Command { pub fn builder () {} } }; proc_macro :: TokenStream :: new () } ãã®ãŸãŸã ãš proc_macro::TokenStream::new() ã«ãã£ãŠçæããã空ã®ããŒã¯ã³åãæ»ãå€ãšããŠè¿ãã®ã§ãçæããããŒã¯ã³åãè¿ãããã«ããŸãããã quote! ãã¯ãã䜿ããš proc_macro::TokenStream ã§ã¯ãªã proc_macro2::TokenStream ãçæãããŸãã proc_macro2::TokenStream 㯠proc_macro2 ã¯ã¬ãŒãã«ãã£ãŠæäŸãããããŒã¯ã³åã§ãã proc_macro::TokenStream ã Rust ã³ã³ãã€ã©ã®äœ¿çšããããŒã¯ã³åã§ãã proc_macro2::TokenStream 㯠proc_macro::TokenStream::from ã§ proc_macro::TokenStream ã«å€æã§ããã®ã§ã以äžã®ããã«ããŠçæããããŒã¯ã³åãè¿ããŸãã #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let _input = parse_macro_input! (input as DeriveInput); let expand = quote! { impl Command { pub fn builder () {} } }; proc_macro :: TokenStream :: from (expand) } ãã®æ®µéã§ã®ãã¯ãã®å®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro :: TokenStream; use quote :: quote; use syn :: {parse_macro_input, DeriveInput}; #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let _input = parse_macro_input! (input as DeriveInput); let expand = quote! { impl Command { pub fn builder () {} } }; proc_macro :: TokenStream :: from (expand) } CommandBuilder ãè¿ãããã« builder 颿°ã®å®è£
ã倿Žãã åã®ã¹ãããã§å®è£
ãã builder 颿°ã¯ç©ºã§ãã®ã§ããã®ãŸãŸã§ã¯äœãããŠãããŸãããæ¬¡ã®æ®µéãšããŠãããŒãã³ãŒãããã CommandBuilder æ§é äœãè¿ãããã« builder 颿°ã®å®è£
ã倿ŽããŠã¿ãŸãããã ãŸã㯠CommandBuilder æ§é äœã®å®çŸ©ã quote! ãã¯ãå
ã«è¿œå ããŸãã let expand = quote! { pub struct CommandBuilder { executable: Option < String > , args: Option < Vec < String >> , env: Option < Vec < String >> , current_dir: Option < Vec < String >> , } impl Command { pub fn builder () {} } }; 次㫠builder 颿°å
ããããã©ã«ãå€ã§ãã£ãŒã«ããåãã CommandBuilder ãè¿ãããã«å€æŽããŸãã CommandBuilder ã®ãã£ãŒã«ãã¯ãã¹ãŠ Optional ãªã®ã§ None ã§åããŠãããŸãã let expand = quote! { ...ç¥... impl Command { pub fn builder () -> CommandBuilder { CommandBuilder { executable: None , args: None , env: None , current_dir: None , } } } }; æçµçãªå®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro :: TokenStream; use quote :: quote; use syn :: {parse_macro_input, DeriveInput}; #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let _input = parse_macro_input! (input as DeriveInput); let expand = quote! { pub struct CommandBuilder { executable: Option < String > , args: Option < Vec < String >> , env: Option < Vec < String >> , current_dir: Option < Vec < String >> , } impl Command { pub fn builder () -> CommandBuilder { CommandBuilder { executable: None , args: None , env: None , current_dir: None , } } } }; proc_macro :: TokenStream :: from (expand) } æ§é äœåããã£ãŒã«ãåã«å¿ãã Builder æ§é äœãçæãã ããŠããããŸã§ã§ Command æ§é äœçšã® Builder æ§é äœãè¿ããã¯ããã§ããŸããããã ããã®ãŸãŸã ãšã»ãã®æ§é äœã«å¯ŸããŠäœ¿çšã§ããŸãããderive ãã¯ããé©çšãããæ§é äœã®ååããã£ãŒã«ãã«å¿ããŠé©ããå®è£
ãäžããããã«ãããã§ããBuilder æ§é äœã®èŠä»¶ããŸãšãããšä»¥äžã®ããã«ãªããŸãã æ§é äœã®åå㯠derive ãã¯ãã®é©çšãããæ§é äœåã®æ«å°Ÿã« Builder ãä»ãããã®ãšãã ãã£ãŒã«ãå㯠derive ãã¯ãã®é©çšãããæ§é äœã®ãã£ãŒã«ãåãšåããã®ã䜿ã ãã£ãŒã«ãã®å㯠derive ãã¯ãã®é©çšãããæ§é äœã®ãã£ãŒã«ãã®åã Optional ã«ãããã®ãšãã ãã®ãããªèŠä»¶ãæºããããã«ã¯ derive ãã¯ããé©çšãããæ§é äœã®æ
å ±ãååŸããå¿
èŠããããŸããã§ã¯ãã©ãããååŸããã°è¯ãã®ã§ãããããderive ãã¯ãã®å
¥åãããŒã¹ãããšä»¥äžã®ãã㪠syn::DeriveInput æ§é äœãåŸãããã®ã§ãããå®ã¯ãã®æ§é äœã®äžã«å¿
èŠãªæ
å ±ãå
¥ã£ãŠããŸãã pub struct DeriveInput { pub attrs: Vec < Attribute > , pub vis: Visibility, pub ident: Ident, pub generics: Generics, pub data: Data, } ãã®ã¹ãããã«é¢ä¿ããéšåã¯ä»¥äžã® 3 ã€ã§ãã vis ã¯æ§é äœã®å¯èŠæ§ã®æ
å ±ãä¿æããŠããŸã ident 㯠identifier ã®ç¥ã§ã倿°åãªã©ã® Rust ã³ãŒãäžã®èå¥åã®æ
å ±ãä¿æããŠããŸããããã§ã¯ derive ãã¯ããä»äžãããæ§é äœ/enum ã®ååãä¿æããŠããŸã data ã¯æ§é äœã®ä¿æãããã£ãŒã«ãã®æ
å ±ãæã£ãŠããŸã æ§é äœå㯠ident ã䜿ãã°ååŸã§ãããã§ããderive ãã¯ãã®äœ¿ãããæ§é äœåã®æ«å°Ÿã« Builder ãä»ãããã®ã Builder æ§é äœã®ååã«ãããã®ã§ã format_ident! ãã¯ãã䜿ã£ãŠæ«å°Ÿã« Builder ãçµåããŸãã format_ident! ãã¯ãã¯ãã®ããã«æ°ããªèå¥åãäœããšãã«äœ¿çšããŸãã詳ãã㯠quote ã¯ã¬ãŒãã® GitHub ãªããžã㪠ãã芧ãã ããã let ident = input.ident; let builder_name = format_ident! ( "{}Builder" , ident); æ§é äœåã«ã€ããŠã¯ããã§è¯ãããã§ããæ¬¡ã¯ãã£ãŒã«ãåãšåãååŸããŸãããããã£ãŒã«ãã®æ
å ±ã¯ data ãã£ãŒã«ãã«å
¥ã£ãŠããã®ã§ããã syn::Data ã¯ä»¥äžã®ãã㪠enum ã§ããä»åæ±ãã®ã¯æ§é äœãªã®ã§ DataStruct æ§é äœã®äžèº«ãèŠãŠã¿ãŸãããã pub enum Data { Struct (DataStruct), Enum (DataEnum), Union (DataUnion), } syn::DataStruct ã¯ãããªã£ãŠããŸãã fields ããã£ãŒã«ãã®æ
å ±ãæã£ãŠããŸãã pub struct DataStruct { pub struct_token: Struct, pub fields: Fields, pub semi_token: Option < Semi > , } Fields ã¯ä»¥äžã®ãã㪠enum ã§ãã Named ãååã®ã€ãããã£ãŒã«ãã§ããä»å㯠Unit æ§é äœãã¿ãã«æ§é äœã¯ãµããŒãããªãã®ã§ Named ã ãèŠãŠã¿ãŸãããã pub enum Fields { Named (FieldsNamed), Unnamed (FieldsUnnamed), Unit, } FieldsNamed ã¯ãã£ãŒã«ãæ
å ±ãæã£ãŠãã Field æ§é äœãè€æ°ä¿æããŠããã named.iter() ã§ Field æ§é äœã®ã€ã³ã¹ã¿ã³ã¹ãã€ãã¬ãŒãã§ããŸãã pub struct FieldsNamed { pub brace_token: Brace, pub named: Punctuated < Field, Comma > , } syn::Field æ§é äœã¯ãããªã£ãŠããŸãã ident ããã£ãŒã«ãåã§ ty ãåã®æ
å ±ãæã£ãŠããŸããããããå¿
èŠãªæ
å ±ãŸã§ãã©ãçããŸããã pub struct Field { pub attrs: Vec < Attribute > , pub vis: Visibility, pub ident: Option < Ident > , pub colon_token: Option < Colon > , pub ty: Type, } ãã¿ãŒã³ãããã䜿ã£ãŠå¿
èŠãªæ
å ±ã ããåãåºããŸãããµããŒãããªãã±ãŒã¹ã«ã€ããŠã¯ä»ã¯ panic! ãããŠãããŸãããïŒåŸã®ã¹ãããã§ã¡ãããšã³ã³ãã€ã«ãšã©ãŒãåºãããã«ããŸãïŒããã®åŸã§äœ¿ããããããã« ident ãš ty 㯠Vec ã«æ ŒçŽããŠãããŸãã let (idents, types): ( Vec < Ident > , Vec < Type > ) = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields .named . into_iter () . map ( | field | { let ident = field.ident; let ty = field.ty; (ident. unwrap (), ty) }) . unzip (), _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "expects struct" ), }; ããŠãå¿
èŠãªãã®ã¯ãã¹ãŠæºåã§ããŸããããããã䜿ã£ãŠãã¯ãã®åºåãšããŠè¿ãããŒã¯ã³åãçæããŸãããã ãã®åã®ã¹ãããã§ quote! ãã¯ãã«ã€ã㊠Rust ã®ã³ãŒããšïŒã»ãŒïŒåãæ§æã§èšè¿°ããããšãã§ã ãšèª¬æããŸãããã quote! ãã¯ãã®å
éšã§ã¯ç¹æ®ãªèšæ³ã䜿ãããšãã§ããŸããããšãã°ã quote::ToTokens ãã¬ã€ããå®è£
ãã倿°ã« quote! ãã¯ãã®äžã§ # ãã€ãããšãã®å€æ°ã®å
å®¹ãæ¿å
¥ãããŸããããã䜿ã£ãŠé©åãªååãåã®ãã£ãŒã«ããæã£ãæ§é äœãåçã«çæããŸããä»ãŸã§ã«åºãŠãã syn::Ident ãªã©ã®æ§é äœã¯ quote::ToTokens ãã¬ã€ããå®è£
ããŠããã®ã§ç¹ã«æ°ã«ãã䜿ãããšãã§ããŸãã ããã䜿ã£ãŠ CommandBuilder æ§é äœã®æ§é äœåãæžãçŽããšä»¥äžã®ããã«ãªããŸãã quote! { #vis struct #builder_name { executable: Option < String > , args: Option < Vec < String >> , env: Option < Vec < String >> , current_dir: Option < Vec < String >> , } } ãŸãã quote! ãã¯ãã®äžã§ã¯ #(...)* ã®ãããªåœ¢ã§ IntoIterator ãå®è£
ããåã®å€æ°ãç¹°ãè¿ããŠæ¿å
¥ã§ããŸããããã䜿ã£ãŠ CommandBuilder æ§é äœã®ãã£ãŒã«ãåãæžãæãããšä»¥äžã®ããã«ãªããŸãã idents ãš types ã¯ãããããã£ãŒã«ãåãšåãæ ŒçŽãããã¯ã¿ã§ãã®ã§ã以äžã®ããã«ããŠãã¯ã¿ã®åå€ã«ã€ããŠç¹°ãè¿ãå±éã§ããŸãã ) ãš * ã®éã«æåãèšè¿°ãããšãã®æåã§åºåã£ãŠå±éããŠãããã®ã§ã以äžã§ã¯ #(...),* ã®ããã«ããŠæ§é äœã®ãã£ãŒã«ãããã¯ã¿ããçæããŠããŸãã quote! { #vis struct #builder_name { #( #idents : Option < #types > ), * } } ããŠãããã§ããŒãã³ãŒããããæ§é äœåããã£ãŒã«ãåãªã©ããã¹ãŠåãé€ãããšãã§ããŸããã åãããã« builder 颿°ãæžãæãããšä»¥äžã®ããã«ãªããŸãã quote! { #vis struct #builder_name { #( #idents : Option < #types > ), * } impl #ident { pub fn builder () -> #builder_name { #builder_name { #( #idents : None ), * } } } } 以äžããŸãšãããšæçµçãªãã¯ãã®å®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro :: TokenStream; use quote :: {format_ident, quote}; use syn :: {parse_macro_input, Data, DeriveInput, Fields, Ident, Type}; #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let (idents, types): ( Vec < Ident > , Vec < Type > ) = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields .named . into_iter () . map ( | field | { let ident = field.ident; let ty = field.ty; (ident. unwrap (), ty) }) . unzip (), _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "expects struct" ), }; let expand = quote! { #vis struct #builder_name { #( #idents : Option < #types > ), * } impl #ident { pub fn builder () -> #builder_name { #builder_name { #( #idents : None ), * } } } }; proc_macro :: TokenStream :: from (expand) } 03-call-setters ç®æš CommandBuilder æ§é äœã®åãã£ãŒã«ãã«å¯Ÿãã setter ã¡ãœãããäœã å®è£
æ¹é å
·äœçãªå®è£
ããèãããšãããããã§ãã Command 颿°ã® executable ãã£ãŒã«ããäŸã«ãšããš setter ã¯ä»¥äžã®ããã«ãªãã®ã§ããããåã®ã¹ããããšåæ§ã倿°ã®å±éãç¹°ãè¿ãã䜿ã£ãŠæžãçŽããŸãããã pub fn executable ( &mut self , executable: String ) -> &mut Self { self .executable = executable self } å®è£
å®è£
æ¹éã§æžããå
·äœçãªãã£ãŒã«ãã®å®è£
ãæžãçŽããŸãããã倿°ã®å±éãç¹°ãè¿ãã®æžãæ¹ã¯ãã§ã«èª¬æããéãã§ãã #(...)* ã®æ¬åŒ§å
ã«ã¯å€æ°ä»¥å€ãå
¥ããããšãã§ããŸããä»åã¯é¢æ°ã®å®è£
èªäœã #(...)* ã§ç¹°ãè¿ããŠããã°è¯ãã§ãããã impl #builder_name { #( pub fn #idents ( &mut self , #idents : #types ) -> &mut Self { self . #idents = Some ( #idents ); self }) * } æçµçãªå®è£
ã¯ä»¥äžã®éãã§ãã use proc_macro :: TokenStream; use quote :: {format_ident, quote}; use syn :: {parse_macro_input, Data, DeriveInput, Fields, Ident, Type}; #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let (idents, types): ( Vec < Ident > , Vec < Type > ) = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields .named . into_iter () . map ( | field | { let ident = field.ident; let ty = field.ty; (ident. unwrap (), ty) }) . unzip (), _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "expects struct" ), }; let expand = quote! { #vis struct #builder_name { #( #idents : Option < #types > ), * } impl #builder_name { #( pub fn #idents ( &mut self , #idents : #types ) -> &mut Self { self . #idents = Some ( #idents ); self }) * } impl #ident { pub fn builder () -> #builder_name { #builder_name { #( #idents : None ), * } } } }; proc_macro :: TokenStream :: from (expand) } 04-call-build ç®æš Command æ§é äœã®ã€ã³ã¹ã¿ã³ã¹ãè¿ã build ã¡ãœããã CommandBuilder æ§é äœã«å®è£
ãã CommandBuilder ã®åãã£ãŒã«ãã None ã®å Žåã¯ãšã©ãŒãè¿ãããã«ãã å®è£
æ¹é ãããå
·äœçãªå®è£
ããèãããšããããããã§ãã Command æ§é äœã®å Žåã¯ä»¥äžã®ããã«ãªãã®ã§ãããã倿°ã®å±éãç¹°ãè¿ãã䜿ã£ãŠæžãçŽããŸãããã impl CommandBuilder { ...ç¥... pub fn build () -> Result < Command, Box < dyn Error >> { if self .executable. is_none () { return Err (...ç¥...) } ...ç¥... if self .current_dir. is_none () { return Err (...ç¥...) } Command { executable: self .executable. clone (). unwrap (), args: self .args. clone (). unwrap (), env: self .env. clone (). unwrap (), current_dir: self .current_dir. clone (). unwrap (), } } } å®è£
ã¬ãŒãç¯ããŸãäœããŸãããã®ããã« quote! ã䜿ã£ãŠéšåçã«ããŒã¯ã³åãäœã£ãŠåŸã§çµã¿åãããããšãã§ããŸãã let checks = idents. iter (). map ( | ident | { let err = format! ( "Required field '{}' is missing" , ident. to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()) } } }); åã®ã¹ããããšåãããã«ã㊠build 颿°ãäœããŸããäžã§äœã£ãã¬ãŒãç¯ã #(#checks)* ã§å±éããŠããŸãã let expand = quote! { ...ç¥... pub fn build ( &mut self ) -> Result < #ident , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #ident { #( #idents : self . #idents . clone (). unwrap ()), * }) } ...ç¥... } æçµçãªå®è£
ã¯ä»¥äžã®éãã§ãã use proc_macro :: TokenStream; use quote :: {format_ident, quote}; use syn :: {parse_macro_input, Data, DeriveInput, Fields, Ident, Type}; #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let (idents, types): ( Vec < Ident > , Vec < Type > ) = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields .named . into_iter () . map ( | field | { let ident = field.ident; let ty = field.ty; (ident. unwrap (), ty) }) . unzip (), _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "expects struct" ), }; let checks = idents. iter (). map ( | ident | { let err = format! ( "Required field '{}' is missing" , ident. to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()) } } }); let expand = quote! { #vis struct #builder_name { #( #idents : Option < #types > ), * } impl #builder_name { #( pub fn #idents ( &mut self , #idents : #types ) -> &mut Self { self . #idents = Some ( #idents ); self }) * pub fn build ( &mut self ) -> Result < #ident , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #ident { #( #idents : self . #idents . clone (). unwrap ()), * }) } } impl #ident { pub fn builder () -> #builder_name { #builder_name { #( #idents : None ), * } } } }; proc_macro :: TokenStream :: from (expand) } 05-method-chaining ç®æš CommandBuilder æ§é äœã§ã¡ãœãããã§ãŒã³ã䜿ããããã«ãã å®è£
æ¹é 03-call-setters ã¹ãããã§å®è£
ãã setter 㯠&mut Self ãè¿ãããã«ãªã£ãŠããã®ã§ãå®ã¯ãã§ã«ã¡ãœãããã§ãŒã³ã䜿ããããã«ãªã£ãŠããŸãããã®ããããã®ã¹ãããã§ã¯è¿œå ã®å®è£
ãããå¿
èŠã¯ãããŸããã å®è£
ãã®ã¹ãããã§ã¯è¿œå ã®å®è£
ã¯å¿
èŠãããŸããããã®ã¹ãããçšã®ãã¹ããå®è¡ããŠå€æŽãªãã§ãã¹ãããã¹ããããšããã§ãã¯ããŠã¿ãŸãããã ãŸãšã åç·šã§ã¯ Builder ãã¿ãŒã³ãå®çŸããã®ã«æäœéå¿
èŠãªæ©èœãæã£ããã¯ããäœããŸããã ãããããŸã Optional ãªãã£ãŒã«ãã®åãæ±ãã«äžååãªç¹ããã£ããããã¯ã¿åã®ãã£ãŒã«ãã®åãæ±ãã«æ¹åã®äœå°ããã£ããããŸããåŸç·šã§ã¯ãã®ãããã®æ©èœãå®è£
ããŠãããŸãã åŸç·šãžç¶ãã åèæç® proc-macro-workshop/builder ã«åãçµã Rust ã® procedural macro ãæã£ãŠé»éè¡åž«ã«ãªãããproc-macro-workshop ã®çŽ¹ä» å®è·µ Rust ããã°ã©ãã³ã°å
¥é syn - Docs.rs quote - Docs.rs