Rustの最新リリースであるバージョン1.30では、プロシージャマクロが拡張され、新しい属性や関数風のマクロが定義可能になった。また、合理化と簡素化により、Rustモジュールシステムの効率が向上している。
Rust 1.30では、“属性型手続き型マクロ(attribute-like procedural macros)”と“関数型手続き型マクロ(function-like procedural macros)”という、2つのタイプの手続き型マクロが新たに導入されている。手続き型マクロはRustメタプログラミングの基礎であり、プログラム構文ツリーの操作を可能にする。この点において、手続き型マクロは宣言型マクロよりはるかに強力で、パターンマッチングに基づいた、より複雑なコードの省略形を定義するメカニズムを提供する。
属性型手続き型マクロは、既存の派生マクロ(derive macros)に近いが、新たに属性を定義できるという点でより柔軟であり、構造体や列挙型に加えて関数にも適用することができる。例として、属性型マクロを使用することで、HTTPルーティングを定義するroute
属性が指定可能になる。
// use of route procedural macro
#[route(GET, "/")]
fn index() {
...
}
// procedural macro defining route
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
// attr receives the GET, "/" part of the macro
// item receives fn index () { ...
}
同じように、関数型手続き型マクロでは、関数のような外観のマクロを定義することができる。例えば、
// parse an SQL statement
let sql = sql!(SELECT * FROM posts WHERE id=1);
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
いずれの例でも、TokenStream
は、属性が適用される構文ツリーあるいは属性/関数定義を表している。route
/sql
関数は、受信した構文ツリーを新たな構文ツリーに変換した上で呼び出し元に返す。すなわち、新たな実行コードを生成することができる。
Rust 1.30ではまた、use
マクロが一部変更されたことにより、Rustモジュールシステムの開発者エクスペリエンスが向上している。最初に、use
をマクロ定義で使用できるようになった。これに伴ってmacro_user
アノテーションは廃止される。
// old:
#[macro_use]
extern crate serde_json;
// new:
extern crate serde_json;
use serde_json::json;
また、外部クレートのレジリエンシが向上しており、すべてのextern crate
ディレクティブに対して、モジュールプレリュードを含むすべての名前空間への参照がチェックされ、一致するものがあればそれを用いるようになった。これにより、モジュール階層全体を対象として機能するようになる。これまでは、モジュール内部でextern
を明示的に使用するか、あるいは以下の例に示すような::extern_name
構文を使用する必要があった。
extern crate serde_json;
fn main() {
let json = serde_json::from_str("..."); // OK
}
mod foo {
// to use serde_json in this module you explicitly use it
use serde_json;
fn bar() {
let json = serde_json::from_str("...");
}
fn baz() {
// alternatively, you fully qualify the external module name
let json = ::serde_json::from_str("...");
}
最後に、use
でモジュールパスを解釈する方法の一貫性が向上した。crate
キーワードを使って、crateルートの開始点としたいモジュールパスを指定することが可能になった。1.30より前はこれがモジュールパスの既定値だったが、アイテムを直接参照するパスはローカルパスから開始される。
mod foo {
pub fn bar() {
// ...
}
}
mod baz {
pub fn qux() {
// old
::foo::bar();
// does not work, which is different than with `use`:
// foo::bar();
// new
crate::foo::bar();
}
}
その他にもRust 1.30では、次のような点が変更されている。
r#for
のようにr#
を先頭に付けることで、キーワードを識別子として使用できるようになった。この変更は主に、Rust 2018で新たなキーワードが導入されるという事実に基づくもので、新たなキーワードを変数や関数名として使用している既存コードを変換する仕組みとして利用することができる。no_std
を指定して、標準ライブラリを使用しないアプリケーションの構築が可能になった。これまでは、パニックハンドラを定義することができないため、no_std
でライブラリを構築することのみが可能であった。
Rustディストリビューションの更新は、$ rustup update stable
を使って行うことができる。 Rust 1.30の詳細はリリースノートで確認してほしい。
この記事を評価
- 編集者評
- 編集長アクション