RustでRESTサヌビスを䜜成したす。 パヌト5ハンドラヌ、リファクタリング、およびマクロ

みなさんこんにちは

Rustで匕き続きWebサヌビスを䜜成したす。 目次

パヌト1プロトタむプ
パヌト2INIを読みたす。 倚錆
パヌト3コン゜ヌルからデヌタベヌスを曎新する
パヌト4REST APIぞの移行
パヌト5これハンドラヌ、リファクタリング、およびマクロ

次に、実際のAPIリク゚ストハンドラを芋お、以前の怖いコヌドを曞き換えたす。 䞀般に、これはシリヌズの最埌の蚘事なので、リファクタリング、スタむル、マクロ、そしおすべおすべおがありたす。 これが最も長い郚分です。

Arcを2回クロヌン化した理由


APIパスを調敎するコヌドは次のようになりたす。

let sdb = Arc::new(Mutex::new(db)); let mut router = router::Router::new(); { let sdb_ = sdb.clone(); router.get("/api/v1/records", move |req: &mut Request| handlers::get_records(sdb_.clone(), req)); } { let sdb_ = sdb.clone(); router.get("/api/v1/records/:id", move |req: &mut Request| handlers::get_record(sdb_.clone(), req)); } 
 

手始めに、ハンドラヌ自䜓。 ここで、たずえば、ハンドラヌ:: get_records

ハンドラヌ:: get_records
 pub fn get_records(sdb: Arc<Mutex<Connection>>, req: &mut Request) -> IronResult<Response> { let url = req.url.clone().into_generic_url(); let mut name: Option<String> = None; if let Some(qp) = url.query_pairs() { for (key, value) in qp { match (&key[..], value) { ("name", n) => { if let None = name { name = Some(n); } else { return Ok(Response::with((status::BadRequest, "passed name in query more than once"))); } } _ => return Ok(Response::with((status::BadRequest, "unexpected query parameters"))), } } } else { return Ok(Response::with((status::BadRequest, "passed names don't parse as application/x-www-form-urlencoded or there are no parameters"))); } let json_records; if let Ok(recs) = ::db::read(sdb, name.as_ref().map(|s| &s[..])) { use rustc_serialize::json; if let Ok(json) = json::encode(&recs) { json_records = Some(json); } else { return Ok(Response::with((status::InternalServerError, "couldn't convert records to JSON"))); } } else { return Ok(Response::with((status::InternalServerError, "couldn't read records from database"))); } let content_type = Mime( TopLevel::Application, SubLevel::Json, Vec::new()); Ok(Response::with( (content_type, status::Ok, json_records.unwrap()))) } 


その眲名が、クロヌゞャヌ内のデヌタベヌスでArcのクロヌンを䜜成する必芁があった理由です。

 pub fn get_records(sdb: Arc<Mutex<Connection>>, req: &mut Request) -> IronResult<Response> { 

ご芧のずおり、Arcは倀によっお぀たり、所有暩付きでここに枡されたすが、これは単玔にコピヌされるタむプではありたせん。 このため、Arcを耇補しおハンドラヌに枡したした。

ハンドラヌで䜕が起こるか


䞀般に、ハンドラヌは同じタむプであるため、get_recordsを比范的詳现に芋るだけです。これは最も耇雑です。 ハンドラヌはパタヌンマッチングを積極的に䜿甚しお゚ラヌ状況を刀断したす。

最初に、 Url Ironからrust-url圢匏のUrlを取埗したす。

  let url = req.url.clone().into_generic_url(); 

これを実行しおquery_pairsメ゜ッドを䜿甚したす。このメ゜ッドは、URLをapplication / x-www-form-urlencodedデヌタずしお解析し、堎合によっおはキヌず倀のペアの反埩子を返したす。

させお


次に、「if let」ずいう新しい構文を瀺し、その本質を説明したす。

  if let Some(qp) = url.query_pairs() { for (key, value) in qp { 

この゚ントリの意味をすでに掚枬しおいる可胜性がありたす。 if letステヌトメントはパタヌンずの䞀臎を詊み、成功した堎合、if letの埌のブロックに実行を枡したす。 このブロックでは、倀を関連付けたばかりの名前この堎合はqpを䜿甚できたす。 倀をテンプレヌトず䞀臎させるこずができなかった堎合query_pairsがNoneを返したした、elseブランチが実行されたす-通垞のifのように芋えたす。

無効なHTTPステヌタスを返す


したがっお、むテレヌタが返されなかった堎合、これぱラヌです。

  } else { return Ok(Response::with((status::BadRequest, “passed names don't parse as application/x-www-form-urlencoded or there are no parameters”))); } 

ここに、サヌバヌの応答を説明するかっこ内のタプルがありたすHTTPステヌタスずメッセヌゞ。

リク゚ストパラメヌタヌを取埗する


むテレヌタが返された堎合、それを回避しお名前パラメヌタを取埗し、名前倉数に保存したす。

  let mut name: Option<String> = None; if let Some(qp) = url.query_pairs() { for (key, value) in qp { match (&key[..], value) { ("name", n) => { if let None = name { name = Some(n); } else { return Ok(Response::with((status::BadRequest, "passed name in query more than once"))); } } _ => return Ok(Response::with((status::BadRequest, "unexpected query parameters"))), } } } 

ここでルヌプは、むテレヌタをバむパスし、ペアのベクトルから目的の芁玠を匕き出し、所有暩に問題がないようにするために必芁です。 しかし、実際には、名前ず呌ばれる芁求パラメヌタヌが1぀だけ䞎えられおいない状況ぱラヌです。 ルヌプを削陀しおみたしょう。

パラメヌタヌによっおサむクルを削陀したす


.query_pairsは、実際にはOption <Vec <String、String>>を返したす。 したがっお、ベクトルの長さず単䞀のパラメヌタヌの名前を簡単に確認できたす。

  let mut name: Option<String> = None; if let Some(mut qp) = url.query_pairs() { if qp.len() != 1 { return Ok(Response::with((status::BadRequest, "passed more than one parameter or no parameters at all"))); } let (key, value) = qp.pop().unwrap(); if key == "name" { name = Some(value); } } else { 

ここで、ベクトルを迂回するのではなく、その長さを確認し、興味のあるパラメヌタヌを右に回したす。

ここには重芁なポむントがありたす

  let (key, value) = qp.pop().unwrap(); 

popを䜿甚するこずが重芁です-所有暩を持぀ベクタヌ芁玠を提䟛したす。 通垞のむンデックス呌び出しqp [0]はリンクを提䟛し、倀をペアからSome倀に移動しおすべおを名前に入れるこずはできたせん。

文字列比范がstrで機胜するのはなぜですか


たた、ベクタヌのペアString、Stringに栌玍されおいるこずにも泚意しおください。 しかし、キヌを「名前」ず盎接比范したす-文字列リテラル

  if key == "name" { name = Some(value); } 

芚えおいるように、それは型 'static strです。 これが機胜するのは、StringがPartialEqを実装しお 'a strず比范するためです。

 impl<'a> PartialEq<&'a str> for String 

したがっお、ここでは型倉換は行われたせん。

そのような型がなかった堎合、スラむス構文を䜿甚しお文字列をstrに倉換できたす。キヌ[..]は、文字列党䜓に沿っおスラむスを返したす。぀たり、 リンクは同じコンテンツのstrです。

次に、デヌタベヌスぞの実際のアクセスを行いたす。

初期化されおいない倉数-危険ですか


たず、RESTアクセスポむントが返すJSONレコヌドの名前を宣蚀したす。

  let json_records; 

うヌん、倀で初期化したせんか 自分の足で撮圱したいですか

いいえ、Rustは宣蚀された名前を初期化するたで䜿甚したせん。 たずえば、そのようなコヌドでは

 fn main() { let a; if true { a = 5; } else { println!("{}", a); } } 

゚ラヌが発生したす

 test.rs:6:24: 6:25 error: use of possibly uninitialized variable: `a` [E0381] test.rs:6 println!("{}", a); ^ 


デヌタベヌスからレコヌドを読み取りたす。 Option :: mapを䜿甚したす


次に、デヌタベヌスから゚ントリを読み取りたす。

  if let Ok(recs) = ::db::read(sdb, name.as_ref().map(|s| &s[..])) { 

議論の䞭で奇劙なこずが起こっおいるのはなぜですか

  name.as_ref().map(|s| &s[..]) 

これから説明したす。 たず、:: db :: readの眲名を芋おください

 pub fn read(sdb: Arc<Mutex<Connection>>, name: Option<&str>) -> Result<Vec<Record>, ()> { 

ご芧のずおり、名前はOption<&str>圢匏で名前を取りOption<&str> 。 名前のタむプはOption<String>です。 しかし、それは重芁ではありたせん.as_ref()メ゜ッドは、 Option<T>をOption<&T>倉換しOption<&T> -このようにしお、 Option<&String>を取埗したす。

残念ながら、なぜなら &Stringはオプションにラップされ、自動的に&str倉換されたせん。 したがっお、匿名関数では䞊蚘のスラむス構文を䜿甚したす。

  .map(|s| &s[..]) 

.mapは、関数をOptionのコンテンツに適甚し、TをOption<T>から他のタむプに倉換したす。 この堎合、 &Stringを&str倉換し&str 。 これはHaskell fmap :: Functor f => (a -> b) -> fa -> fb䌌おいたす。

埮劙な点がありたす。 name: Option<T>で.mapをすぐに呌び出せたせんでした。なぜなら、 リンクは、呌び出されたずきに関数パラメヌタヌのスコヌプ内でのみ有効になりたす。 この堎合、クロヌゞャ内にリンクを取埗し、クロヌゞャが存続しおいる間のみリンクが存続したす。 ただし、どこにも保存されず、パラメヌタヌを関数に枡すず砎棄されたす。 このようなリンクは䞀時オブゞェクトになりたす。

 handlers.rs:25:5325:54゚ラヌ `s`は十分な長さではありたせん
 handlers.rs:25 if Okrecs= :: db :: readsdb、name.map| s |s [..]{
                                                                    ^
 handlers.rs:25:2325:60泚参照は25:22の呌び出しに察しお有効でなければなりたせん...
 handlers.rs:25 if Okrecs= :: db :: readsdb、name.map| s |s [..]{
                                      ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 handlers.rs:25:5225:58泚...ただし、借甚した倀は25:51の関数のパラメヌタヌのスコヌプでのみ有効です
 handlers.rs:25 if Okrecs= :: db :: readsdb、name.map| s |s [..]{
                                                                   ^ ~~~~~ 

.as_refの堎合、Option自䜓が存続しおいる間にリンクが存続するため、すべおが機胜したす。

しかし、マルチスレッドに぀いおはどうでしょうか


:: db ::を芋お、賞賛されおいるデヌタレヌシング保護がどのように機胜するかを読んでみたしょう。

  if let Ok(rs) = show(&*sdb.lock().unwrap(), name) { Ok(rs) } else { Err(()) } 

showを呌び出したい

 pub fn show(db: &Connection, arg: Option<&str>) -> ::postgres::Result<Vec<Record>> { 

この関数はConnectionぞのリンクを受け入れ、 Arc<Mutex<Connection>>たす。 参照カりンタを展開しおミュヌテックスをキャプチャしない限り、関心のあるデヌタベヌス接続にアクセスできたせん。 型システムは無効な状態を衚珟できないようにしたす。

ほずんど魔法


そのため、ミュヌテックスを匕き継ぎたいです。 参照カりンタヌにネストされおいたす。 ここでは、2぀のこずが関係したす。逆参照時の倉換ず、メ゜ッド呌び出し時の自動逆参照です。

今のずころ、奇劙な*を無芖しお、sdb.lock自䜓を芋おください。 sdbはArcですが、 Arc<T>はDeref<T>実装しおいたす。

 impl<T> Deref for Arc<T> where T: ?Sized type Target = T fn deref(&self) -> &T 

したがっお、必芁に応じおArc<T>は自動的にTに倉換されたす。 これにより、 &Mutex<Connection>が埗られたす。

次に、メ゜ッドを呌び出すずきに自動逆参照が䜜甚したす。 芁するに、コンパむラはメ゜ッド呌び出しず同数の逆参照を挿入したす。

以䞋に簡単な䟋を瀺したす。

 struct Foo; impl Foo { fn foo(&self) { println!("Foo"); } } let f = Foo; f.foo(); (&f).foo(); (&&f).foo(); (&&&&&&&&f).foo(); 

最埌の4行はすべお同じこずを行いたす。

RAIIを䜿甚したミュヌテックスセヌフリリヌス


Mutex :: lockは、 LockResult<MutexGuard<T>>を返しLockResult<MutexGuard<T>> 。 結果により゚ラヌを凊理でき、 MutexGuard<T>はRAII倀であり、 MutexGuard<T>䜜甚を停止するずすぐにミュヌテックスを自動的に開きたす。

同じ&* MutexGuard<T>をTに倉換しMutexGuard<T> -最初にそれを逆参照しおTを取埗し、次にアドレスを取埗しお通垞のリンクTを取埗したす。

なぜlockがArc<Mutex<Connection>>で盎接機胜し、MutexGuardを手動で倉換する必芁があるのですか ロックはメ゜ッドであり、メ゜ッド呌び出しは実際にリンクを逆参照するだけでなく、䞀郚のリンクを他のリンクに倉換する぀たり、 &*類䌌するためです。 関数に匕数を枡す堎合、これは手動で行う必芁がありたす。

連茉


レコヌドを受け取ったら、それらをJSONにシリアル化したす。 これを行うには、 rustc_serializeを䜿甚したす 。

  use rustc_serialize::json; 

ご芧のずおり、モゞュヌルをグロヌバルにむンポヌトできるだけでなく、単䞀の関数たたはブロックのスコヌプ内でもむンポヌトできたす。 これは、グロヌバル名前空間を詰たらせないようにするのに圹立ちたす。

シリアル化自䜓は、次のコヌドで実行されたす。

  if let Ok(json) = json::encode(&recs) { json_records = Some(json); } ... 

この堎合、シリアラむザヌコヌドは自動的に生成されたす レコヌドの型をシリアラむズ可胜および同時にデシリアラむズ可胜ずしお宣蚀するだけです。

 #[derive(RustcEncodable, RustcDecodable)] pub struct Record { id: Option<i32>, pub name: String, pub phone: String, } 


すべおを送り返したす


最埌に、適切なヘッダヌを䜿甚しおJSONを正しいHTTPでラップしお返したす。

  let content_type = Mime( TopLevel::Application, SubLevel::Json, Vec::new()); Ok(Response::with( (content_type, status::Ok, json_records.unwrap()))) 

残りのハンドラヌも同様に機胜するため、自分で繰り返すのではなく、コヌドをリファクタリングしたす。

䞀般的に、私たちのプログラムは終わりたした これで、すばらしい電話垳をコマンドラむンからだけでなく、流行のWeb APIからも曎新できたす。 動䜜を確認するには、 GitHubの feature-completeタグを䜿甚しおコヌドのバヌゞョンを取埗したす 。

リファクタリングはそれほど耇雑ではありたせん。Rustコヌドも矎しいこずを確信させるために、このプロセスを瀺したす。 機胜を実装するプロセスで私たちが別れた読み取り䞍胜な混乱は、単にラッシュの結果です。 Rustはこれを責めるものではありたせん。゚レガントなコヌドを曞くこずができたす。

クロヌンをクロヌンしたせん


たず、前のパヌトで説明した二重アヌククロヌンに぀いお説明したす。

  { let sdb_ = sdb.clone(); router.get("/api/v1/records", move |req: &mut Request| handlers::get_records(sdb_.clone(), req)); } 

勝぀こずは非垞に簡単です。 ハンドラヌの眲名を倉曎したす:: get_records with

 pub fn get_records(sdb: Arc<Mutex<Connection>>, req: &mut Request) -> IronResult<Response> { 

に

 pub fn get_records(sdb: &Mutex<Connection>, req: &mut Request) -> IronResult<Response> { 

そしお䞀般的に、ハンドラヌずデヌタベヌス関数では、どこでも&Mutex<Connection>を䜿甚したす。 すべお、二重クロヌニングはもう必芁ありたせん。

  { let sdb = sdb.clone(); router.get("/api/v1/records", move |req: &mut Request| handlers::get_records(&*sdb, req)); } 

巚倧なメむン​​も䞀芋の䟡倀がありたす。 すべおのアクションを関数に取り蟌んで、クヌルでコンパクトなメむンを取埗したす。

 fn main() { let (params, sslmode) = params(); let db = Connection::connect(params, &sslmode).unwrap(); init_db(&db); let args: Vec<String> = std::env::args().collect(); match args.get(1) { Some(text) => { match text.as_ref() { "add" => add(&db, &args), "del" => del(&db, &args), "edit" => edit(&db, &args), "show" => show(&db, &args), "help" => println!("{}", HELP), "serve" => serve(db), command => panic!( format!("Invalid command: {}", command)) } } None => panic!("No command supplied"), } } 


rustfmt


最埌に、甘い rustfmt  Rust゜ヌスコヌドフォヌマットナヌティリティはただ完成しおいたせんが、小さなプロゞェクトのコヌドを装食するのにすでに適しおいたす。

リポゞトリのクロヌンを䜜成したら、cargo build --releaseを䜜成し、結果の実行可胜ファむルを$ PATHのどこかにコピヌしたす。 次に、プロゞェクトのルヌトで、

 $ rustfmt src / main.rs 

そしおそれだけです、 プロゞェクト党䜓のコヌドは即座にフォヌマットされたす rustfmtは他のモゞュヌルぞのリンクをたどり、それらもフォヌマットしたす。

gofmtずは異なり、rustfmtを䜿甚するず、゜ヌスを曞き換えるかなり詳现なスタむルを蚭定できたす。

珟圚のデフォルトスタむルは、コンパむラが蚘述されおいるスタむルに䌌おいたす。 ただし、 公匏のスタむルガむドが完成するず、rustfmtも曎新されたす。

この「合理的な」リファクタリングは終わり、始たりたす...物議を醞すものですが、間違いなく楜しいものです。マクロを䜿甚しお同様のコヌドの残りの繰り返しを削陀したしょう。

マクロ


私はどのような繰り返しに぀いお話しおいるのですか これに぀いお

  { let sdb = sdb.clone(); router.get("/api/v1/records", move |req: &mut Request| handlers::get_records(&*sdb, req)); } { let sdb = sdb.clone(); router.get("/api/v1/records/:id", move |req: &mut Request| handlers::get_record(&*sdb, req)); } { let sdb = sdb.clone(); router.post("/api/v1/records", move |req: &mut Request| handlers::add_record(&*sdb, req)); } { let sdb = sdb.clone(); router.put("/api/v1/records/:id", move |req: &mut Request| handlers::update_record(&*sdb, req)); } { let sdb = sdb.clone(); router.delete("/api/v1/records/:id", move |req: &mut Request| handlers::delete_record(&*sdb, req)); } 

明らかに、ここにはコヌドに反映できなかったいく぀かの高レベルの構造がありたす。 これらのブロックは、ルヌタヌが呌び出す必芁のあるメ゜ッドが異なるため、通垞の関数でこれらのオプションをすべお凊理するために、匕数に応じお察応するメ゜ッドを呌び出す列挙によっお照合する必芁がありたす。

これは、䞀般的に蚀えば、オプションであり、このコヌドを職堎で曞いた堎合、おそらくこれを実行しようずしたすが、ここでは楜しんでおり、Rustでマクロを詊しおみたかったのです。 それでは始めたしょう。

たず、ここでの繰り返し構造は、Arcを耇補しおからステヌトメントを実行するブロックです。 察応するマクロを曞きたしょう

 macro_rules! clone_pass_bound { ($arc:ident, $stmt:stmt) => { { let $arc = $arc.clone(); $stmt; } } } 

最初の行は、clone_pass_boundずいうマクロの定矩を開始したこずを瀺しおいたす。 バカな名前ですが、うたくいきたせんでした。 これはそれ自䜓が症状であり、おそらく動䜜䞭のコヌドでは実行すべきではありたせん。 しかし、さあ-これは今私たちのケヌスではありたせん。

Rustのマクロには型が付けられおおり、2぀の匕数-型識別子の$ arcず型挔算子の$ stmt文、stmtを取りたす。 よく芋るず、䞀臎するマクロの定矩の類䌌性に気付くこずができたす-ここでは、匕数の特定の組み合わせが特定の本䜓ず比范されたす。 マクロには、matchのような倚くのブランチを含めるこずができたす。これは、再垰の堎合に圹立ちたす。

矢印の埌に、2組のブレヌスがありたす。 マクロ蚘述の構文に応じお、いく぀かは-通垞、通垞の䞀臎の堎合ず同様に必芁です。

2番目のペアを䜿甚しお、マクロがブロックに展開されるず蚀いたす。 ブロック内で、sdbを$ arcに眮き換えお、ほが通垞のコヌドを蚘述したす。 これは簡単な䞀般化です。 クロヌニングの埌にオペレヌタヌが続きたす。

これがこのマクロの呌び方です

  clone_pass_bound!( sdb, router.get("/api/v1/records", move |req: &mut Request| handlers::get_records(&*sdb, req))); 

これたでのずころ、ボリュヌムに関しおは䜕も保存しおいたせんが、理解できない電話しか受けおいたせん。 しかし、絶望しないでください-私たちは始たったばかりです

マクロ䞊のマクロ


これで、デヌタベヌスぞの接続、ルヌタヌ、远加するメ゜ッドget、postなど、定矩したハンドラヌの名前の4぀のパラメヌタヌを䜿甚しお1぀のハンドラヌを蚘述できるこずが明らかになりたした。 このためのマクロを曞きたしょう。

 macro_rules! define_handler { ($connection:ident, $router: ident.$method:ident, $route:expr, $handler:path) => { clone_pass_bound!( $connection, $router.$method( $route, move |req: &mut Request| $handler(&*$connection, req))); } } 

ここでは、たず、マクロ呌び出しず通垞のパタヌンマッチングの類䌌性をもう䞀床匷調する䟡倀がありたす。 ご芧のずおり、マクロ匕数の区切り文字はコンマである必芁はありたせん。通垞のコヌドずの類䌌性を高めるために、ルヌタヌずそのメ゜ッドをドットで区切りたした。

次に、すべおの具䜓的な名前をマクロのメタ倉数に眮き換えお、以前のマクロを呌び出したす-それほど怖くも難しくもありたせん。 通垞、これらのマクロは䞡方ずも最初の詊行で䜜成したした。

これで非垞識なマクロを20行䜜成し、最終的にカットしたいコヌドが枛少し始めたした。

  define_handler!(sdb, router.get, "/api/v1/records", handlers::get_records); define_handler!(sdb, router.get, "/api/v1/records/:id", handlers::get_record); define_handler!(sdb, router.post, "/api/v1/records", handlers::add_record); define_handler!(sdb, router.put, "/api/v1/records/:id", handlers::update_record); define_handler!(sdb, router.delete, "/api/v1/records/:id", handlers::delete_record); 

これは制限ではありたせん。最埌のマクロを定矩したす。これにより、定矩が非垞にコンパクトになり、非垞に理解しやすくなりたす。 これで、コヌドの倉曎郚分が非垞に明癜になり、コヌドを完党に也燥させるのに䜕も劚げられたせん。

最埌のマクロでは、わずか1぀の重芁な瞬間がありたす。

マクロドラむブ


最埌のマクロは次のようになりたす。

 macro_rules! define_handlers_family { ($connection:ident, $router:ident, $( [$method:ident, $route:expr, $handler:path]),+ ) => { $( define_handler!($connection, $router.$method, $route, $handler); )+ } } 

かなり小さいです。 重芁な点は、匕数に再珟性を導入したこずです

  ($connection:ident, $router:ident, $( [$method:ident, $route:expr, $handler:path]),+ ) => { 

$( 
 ),+は、このマクロが呌び出されたずきに、括匧で囲たれたグルヌプを1回以䞊繰り返す必芁があるこずを意味したす。 正芏衚珟のように芋えたす。

次は、モンスタヌマクロの本䜓です。 最初に私はこのように曞いた

  define_handler!($connection, $router.$method, $route, $handler); 

コンパむラが反察したこず

 main.rs:134:46: 134:53 error: variable 'method' is still repeating at this depth main.rs:134 define_handler!($connection, $router.$method, $route, $handler); ^~~~~~~ 

先ほど蚀ったように、$メ゜ッド、$ルヌト、$ハンドラを定矩する呌び出しの郚分は繰り返すこずができたす。 Rustマクロには、呌び出しの繰り返しの特定の「レベル」にあるメタ倉数を䜿甚する堎合、同じ繰り返しレベルにする必芁があるずいうルヌルがありたす。

このように考えるこずができたす-マクロ呌び出しパラメヌタヌのタプルは、察応するボディず同時に移動されたす。 ぀たり 1぀のパラメヌタヌセットは1぀のボディに察応する必芁がありたす。 したがっお、マクロの構造を理解しやすくなりたす-䜓は挑戊のようになりたす。

これで、マクロが1぀だけのように蚘録されたした。呌び出しパラメヌタヌは繰り返されたすが、ボディを繰り返すこずはできたせん。 それでは、本䜓に$メ゜ッドを正確に配眮する必芁がありたすか 明確ではありたせん。 ここでは、そのような状況を回避するために、パラメヌタをボディず「段階的に」列挙するルヌルが考案されたした。

私たちにずっお、これはすべお、パラメヌタヌず同じ再珟性修食子でボディをラップする必芁があるこずを意味したす。

  $( define_handler!($connection, $router.$method, $route, $handler); )+ 

これで、$メ゜ッド、$ルヌト、および$ハンドラヌが重耇パラメヌタヌに察応したす。 たた、$接続ず$ルヌタヌは「グロヌバル」です。これらは繰り返し性の修食子ではないため、各本䜓で耇補されたす。

この掗脳の報酬ずしお、APIのすべおのパスの矎しい定矩を取埗したす。

  define_handlers_family!( sdb, router, [get, "/api/v1/records", handlers::get_records], [get, "/api/v1/records/:id", handlers::get_record], [post, "/api/v1/records", handlers::add_record], [put, "/api/v1/records/:id", handlers::update_record], [delete, "/api/v1/records/:id", handlers::delete_record]); 

䞍必芁な重耇はなく、最終バヌゞョンでは、初心者には比范的明確に芋えたす。

Rustのマクロは衛生的であるこずに泚意しおください-マクロ内の名前ず倖郚の名前の衝突は陀倖されたす。

そうそう、ほずんど忘れおしたった-コンパむラオプション--pretty-print = expandはマクロのデバッグに倧いに圹立぀。 そのため、マクロを暙準出力ストリヌムに展開した埌、コヌドを出力したす。 CおよびC ++コンパむラの-Eオプションに䌌おいたす。

じゃあね


それだけです これですべおです。このシリヌズの蚘事は、Webを含むRustでコヌドの構築を開始できるほど十分に語ったず思いたす。

すでにRustで䜕かを始めおいる堎合は、コメントに曞いおください。 たた、途䞭で発生する質問でチャットルヌムにアクセスしたす。圌らはそこで喜んでお手䌝いしたす。

Source: https://habr.com/ru/post/J269903/


All Articles