起こった! Rust 1.15の手続きマクロ

みんな、それは起こった! 後 長い 6週間待った後Rust 1.15はようやくブラックジャックと手続き型マクロで登場しました。







私の控えめな意見では、これはepic 1.0に続く最も重要なリリースです。 このリリースの多くのおいしいものの中で、 手続き型マクロが安定化され、その力、便利さ、安全性で脳が爆発しました。







そして、これは単なる人間に何を与えますか? ほぼ無料の[de]シリアル化データベースへの便利なインターフェイス 、直感的なWebフレームワーク推定コンストラクターなど







はい、まだこの言語に到達していない場合は、今が試してみる時です。特に、1つのコマンドでコンパイラと環境をインストールできるようになったためです。







curl https://sh.rustup.rs -sSf | sh
      
      





ただし、最初にまず最初に。







ちょっとした歴史



長い間、 EqPartialEqOrdPartialOrdDebugCopyCloneなどの標準タイプのみが自動的に表示されました。 これで、カスタムタイプも可能になりました。







手動で実装する代わりに、 #[derive(_)]



と書くだけで十分です。残りはコンパイラーが行います。







 #[derive(Eq, Debug)] struct Point { x: i32, y: i32, }
      
      





Haskellプログラマーにとって、これはすべて(名前を含む)非常によく知られている必要があり、ほぼ同じ場合に適用されます。 derive



属性を見つけた後、コンパイラーは特性のリストを調べて、それらの理解のためにそれらのメソッドの標準セットを実装します。







たとえば、 Eq



場合、 fn eq(&self, other: &Point) -> bool



メソッドは、構造フィールドを順次比較することにより実装されます。 したがって、フィールドが等しい場合、構造は等しいと見なされます。







もちろん、目的の動作がデフォルトの動作と異なる場合、プログラマは次のように自分の手で型の実装を決定できます。







 use std::fmt; impl fmt::Debug for Point { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "My cool point with x: {} and y: {}", self.x, self.y) } }
      
      





場合によっては、自動型推論により、コーディングが大幅に簡素化され、プログラムテキストがより読みやすく簡潔になります。







手続きマクロ



コンパイラーは、Rustほどスマートでも、すべてのメソッドを単独で出力することはできません。 ただし、場合によっては、非標準構造の特定のメソッドを派生させる方法をコンパイラーに伝えてから、アクションの完全な自由を与えたいと思っています。 このアプローチはかなり複雑なメカニズムに適用できるため、プログラマーが手作業でコードを書く必要がなくなります。







手続き型マクロを使用すると、メタプログラミング要素を言語に追加して、シリアル化やクエリ処理などのルーチン操作を大幅に簡素化できます。







さて、あなたは言う、これはすべて素晴らしいですが、例はどこにありますか?







連載



多くの場合、データを別のプロセスに転送したり、ネットワーク経由で送信したり、ディスクに書き込んだりするタスクがあります。 データ構造が単純で、一連のバイトとして簡単に表現できる場合に適しています。







そうでない場合は? データが任意の長さの文字列、配列、ハッシュテーブル、およびBツリーを持つ複雑な構造のセットである場合 何らかの方法で、データをシリアル化する必要があります。







もちろん、コンピューターサイエンスの歴史では、この問題は繰り返し発生し、その答えは通常、 Google Protobufなどのシリアル化ライブラリにあります。







従来、プログラマーは特別な宣言型言語でデータとプロトコルのメタ記述を作成し、その後、ビジネスロジックで既に使用されているコードにすべてをコンパイルします。







この意味で、Rustも例外ではなく、シリアル化のためライブラリが実際にあります。 メタ記述を書く必要はありません。 すべては、言語自体と手続き型マクロのメカニズムによって実装されます。







 //    - #[macro_use] extern crate serde_derive; //   JSON extern crate serde_json; //   #[derive(Serialize, Deserialize, Debug)] struct Point { x: i32, y: i32, } fn main() { //    let point = Point { x: 1, y: 2 }; //     JSON let serialized = serde_json::to_string(&point).unwrap(); //    : serialized = {"x":1,"y":2} println!("serialized = {}", serialized); //   JSON    Point let deserialized: Point = serde_json::from_str(&serialized).unwrap(); // ,  : deserialized = Point { x: 1, y: 2 } println!("deserialized = {:?}", deserialized); }
      
      





JSONに加えて、Serdeライブラリは、URL、XML、Redis、YAML、MessagePack、Pickleなどの他の形式のホストをサポートしています。 箱から出してすぐに Rust 標準ライブラリのすべてのコンテナのシリアル化と逆シリアル化がサポートされます。







魔法のように見えますが、魔法ではありません。 コンパイル段階での固有のイントロスペクションにより、すべてが機能します。 したがって、すべてのエラーはタイムリーにキャッチされ、修正されます。







設定を読む



逆シリアル化といえば。 上記で、JSON文字列を取得し、そこからフィールドを埋めた構造を取得する方法を説明しました。 同じアプローチを使用して、構成ファイルを読み取ることができます。







サポートされている形式の1つでファイルを作成し、1度に1つの鈍い解析と解析パラメーターを作成するのではなく、単純に構成構造に逆シリアル化するだけで十分です。







DBを使用する



もちろん、ビジネスは1つのシリアル化に限定されません。 たとえば、 Dieselライブラリは便利なデータベースインターフェイスを提供します。これは、手続き型マクロとRustのメソッドの自動出力のおかげでも可能になります。







データベースの使用例
 // ... #[derive(Queryable)] pub struct Post { pub id: i32, pub title: String, pub body: String, pub published: bool, } // ... fn main() { let connection = establish_connection(); let results = posts.filter(published.eq(true)) .limit(5) .load::<Post>(&connection) .expect("Error loading posts"); println!("Displaying {} posts", results.len()); for post in results { println!("{}", post.title); println!("----------\n"); println!("{}", post.body); } }
      
      





完全な例は、図書館のウェブサイトで見つけることができます。







ウェブはどうですか?



ユーザーリクエストを処理したいかもしれません。 繰り返しになりますが、この言語機能により、「機能する」直感的なコードを作成できます。







以下は、最も単純なカウンターを実装するRocketフレームワークを使用したコードの例です。







見る
 struct HitCount(AtomicUsize); #[get("/")] fn index(hit_count: State<HitCount>) -> &'static str { hit_count.0.fetch_add(1, Ordering::Relaxed); "Your visit has been recorded!" } #[get("/count")] fn count(hit_count: State<HitCount>) -> String { hit_count.0.load(Ordering::Relaxed).to_string() } fn main() { rocket::ignite() .mount("/", routes![index, count]) .manage(HitCount(AtomicUsize::new(0))) .launch() }
      
      





または、フォームのデータを処理する必要があるかもしれませんか?







 #[derive(FromForm)] struct Task { complete: bool, description: String, } #[post("/todo", data = "<task>")] fn new(task: Form<Task>) -> String { ... }
      
      





結論



一般に、Rustのメタプログラミングメカニズムが非常にうまく機能することが明らかになります。 言語自体がメモリに関して安全であり、競合状態のない安全なマルチスレッドコードを記述できることを覚えていれば、すべてが非常に良くなります。







多くの人が、SerdeとDieselのためだけにナイトビルドを使用しなければならないと不満を述べていたため、これらの機能が安定版の言語で利用できるようになったことを非常に嬉しく思います。 現在、このような問題はありません。







次の記事では、結局これらのマクロを書く方法と、それらの助けを借りて他に何ができるかについて話します。








All Articles