BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース メタプログラミングのサポートとモジュールが改善されたRust 1.30

メタプログラミングのサポートとモジュールが改善されたRust 1.30

原文(投稿日:2018/11/02)へのリンク

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の詳細はリリースノートで確認してほしい。

 
 

この記事を評価

採用ステージ
スタイル
 
 

この記事に星をつける

おすすめ度
スタイル

特集コンテンツ一覧

BT