0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rustマクロ冬期講習Advent Calendar 2024

Day 3

Rustマクロの事前知識②「出力の掟」

Posted at

この記事で伝えたいこと

Rustマクロは構文「パーツ」だけ出力することはできず、基本的に構文要素全体を出力する必要がある

こちらの記事は Rustマクロ冬期講習アドベントカレンダー 3日目の記事です!

2日目の記事で、Rustマクロについて「出力されるトークン木は設置先のASTに組み込まれて有効なものでなければならない」と述べました。

これに伴って、というわけでもないですが、Rustのマクロとして出力できるものと、できないものがあり、今回のRustマクロシリーズ記事が参考としているThe Little Book for Rust Macrosに記載があるためここで紹介したいと思います。

ここから引用の上意訳

(記事の終盤に差し掛かり)最後に重大な意味を持つため最も大切であるポイントを伝えます。RustマクロはASTへと解析されるため、次に示すサポートされた構文要素の箇所にしか記述することができません。Rustマクロを記述できるのは:

  • パターン ( pat )
  • 文 ( stmt )
  • 式 ( expr )
  • アイテム ( item 。これは impl ブロックを含む )
  • 型 ( type )

以上の箇所 のみ になります。

一方で次に示すような箇所ではどう逆立ちしたところでマクロを使用することは不可能です。

  • 識別子
  • マッチアーム (パターンではなく => を含めたアーム全体を指す)
  • 構造体のフィールド

イマイチ何が出力できないかわかりにくいと思うので一言で表すと、「ある構文要素の 一部 だけを出力するマクロ」は作れないことになっています。

コードで表すとマクロで表現可能なのは次の場合です。

Rust (マクロで表現可能)
macro_rules! can {
    (pat) => {
        Some(_) | None
    };
    (stmt) => {
        let _ = "";
    };
    (expr) => {
        10 + 20 + 30
    };
    (item; $name:ident, $s:stmt) => {
        fn $name() { $s }
    };
    (type_) => {
        Option<u32>
    };
}

fn main() {
    let can!(pat) = Some(10);
    can!(stmt);
    let _ = can!(expr) + 40;

    can!(item; hoge, println!("hoge"));
    hoge();

    let _: can!(type_) = Some(10);
}

以下のようなマクロの使い方はできません!

Rust (コンパイルエラー)
macro_rules! cant {
    (ident; $name:ident) => {
        $name
    };
    (match_arm) => {
        Some(_) | None => (),
    };
    (strct_field) => {
        hoge: u32,
    };
}

fn main() {
    struct cant!(ident; Hoge);

    match Some(10) {
        cant!(match_arm)
    }

    struct HogeHoge {
        cant!(strct_field)
    }
}

1つ目も3つ目も生成しているのは構造体全体ではなく構造体の一部で、2つ目も match 式全体ではなくアームだけをマクロで生成しようとしています。

ある構文要素をなすためのパーツであるからといってただちにダメというわけではないです。先ほどの can マクロでも、ある意味で match 式の一部を記述しているものがあります。

許されないのは、そのパーツを抜き出した時に「構文要素として見做せなさそうなもの」です。

  • struct Hoge {}Hoge の部分
  • a => b という何か
  • hoge: u32 という何か

これらは構造体の一部や match 式の一部ではありますが、この断片だけではRustコードとして意味を為しません!Rustのマクロは、このような中途半端な構造は出力できないようになっています。

とはいえ、例えば「列挙体のフィールドの数分 match 式のマッチアームを生成する」マクロというのはしばしば書きたくなります。こういう時は、マクロの出力結果が match 式全体になるようにする、など、部分ではなく全体を出力することで実現しましょう。

まとめ

要は「ヘンテコな出力は許さない」わけです。2日目の記事にも書いたとおり、こういった厳格な性質のおかげで、Rustのマクロは他言語よりも安心して気軽に呼び出すことができます!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?