9
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マクロ冬期講習アドベントカレンダーをやります!❄

Last updated at Posted at 2024-12-02

カレンダーは無事に完成しました!

まとめ記事: Rustマクロ作成チートシート!

Rustマクロ冬期講習アドベントカレンダー

こちらの記事は Rust Advent Calendar 2024 2日目の記事です!

本記事は今後の進捗に伴い編集されます!

あらすじ: 夏期講習( Qiita Engineer Festa 2024 )していました

こんにちわ!Rustの記事を投稿しまくりたいnamniumと申します。今年の夏に 100 Exercises To Learn Rust というRustチュートリアルを題材に記事投稿マラソンに取り組み、なんとか 完走 できました。

Rustの基礎から非同期処理に至るまで広い範囲をカバーしたチュートリアルだったため、完走後はRustの新しい概念に出会っても、自信を持って取り組めるようになったのでした...!

...

...

なったはずでした...orz

Rustのマクロ難しすぎる :persevere:

100 Exercises To Learn Rust では飛ばされていた内容に、マクロ があります。

Rustのマクロは、(仕組みというか思想はかなり違いますが)C言語・C++にあるようなマクロと同様、「コンパイル時に内容を置き換える」メタプログラミングのための仕組みです。

宣言マクロの例
macro_rules! impl_hello {
    ($type_:ty, $name:expr) => {
        impl Hello for $type_ {
            fn hello(&self) {
                println!("Hello, I'm {}!", $name);
            }
        }
    };
}
コンパイル前にこのように展開される(イメージです)
impl_hello!(Hoge, "Hoge");
impl_hello!(Fuga, "Fuga");

// 展開後↓

impl Hello for Hoge {
    fn hello(&self) {
        println!("Hello, I'm {}!", "Hoge");
    }
}

impl Hello for Fuga {
    fn hello(&self) {
        println!("Hello, I'm {}!", "Fuga");
    }
}

マクロを使えばこのようにボイラーテンプレートを減らしたり、Rustの標準文法では記述が煩雑になってしまうような処理を簡潔に書くことができます。

例えばRustでは可変長引数の関数を作ることができない1ためC言語の printf 関数にあたる機能が println! マクロとして与えられています。こんな感じで、マクロはRustを書いたことがある人なら必ず利用したことがある機能かと思います。 vec! には特にお世話になっているでしょう。

...が、自分でマクロを書くとなると、通常のRustプログラミングでは求められない知識が盛り沢山です! ドキュメントがないわけではないですが、いざマクロを書こうとした時に、自分にとってすぐに欲しい情報が得られる状況でもなく、そこで夏にチュートリアルに取り組んだように、このアドベントカレンダーを利用して本格的に取り組むことにしました!

カレンダーの予定 & 進捗

(次の内容は本記事投稿当時のものです。)


Rustマクロ冬期講習アドベントカレンダー

怠惰な筆者のことだからこうなるだろうなと予想していましたが、遅れています!!!

予定日 タイトル・リンク 内容・補足
1日目予定 Rust宣言マクロ クイックスタート 宣言マクロ作成RTAな内容です。
2日目予定 Rust手続きマクロ クイックスタート 手続きマクロ作成RTAな内容です。
3日目以降予定 Rustマクロの概要・分類 そもそもRustマクロはどういう体系なのかについて簡単に整理する内容です。
4日目以降予定 (タイトル未定) The Little Bookの内容
4日目以降予定 (タイトル未定) proc-macro-workshopの内容
.. (未定) ..
14日目 Rustマクロ フラグメント指定子(ident, expr, item, stmt...)チェッカー! フラグメント指定子が覚えられなさすぎるので、覚えやすくするためのツールを作りました(作っています)。そのツールとフラグメント指定子についての説明です。
.. (未定) ..
25日目予定 Rustマクロチートシート アドベントカレンダーに投稿した内容のうち、特に有益と思われる情報を見やすくまとめる予定です。

筆者オリジナルの内容もいくつか設けたいとは考えていますが、基本的には以下2つのドキュメントの内容を網羅していく所存です!

  • The Little Book of Rust Macros
    • 宣言的マクロ・手続きマクロ双方について基本的なことが書かれた内容です。主に宣言的マクロの方について網羅されています。
  • proc-macro-workshop
    • 手続きマクロについて5つのプロジェクトに取り組むことで、「deriveマクロ」「属性風マクロ」「関数風マクロ」をマスターできるワークショップです。なかなかハード!

Little Bookの方は一通り読んだのですがワークショップの方が途中のためカレンダーが遅れてしまっています。 :sweat_smile: めげずに頑張っていこうと思います!というわけで、よろしくお願いします!


(ここからは追記した内容です。)

2024年が終わり、2025年もひと月が経った頃、ようやく最後の記事を掲載して完成しました!

Rustマクロ作成チートシート!

以下、全体の目次です!

投稿日 タイトル・リンク
1日目 Rust マクロの分類
2日目 Rustマクロの事前知識①「入出力はトークン木」
3日目 Rustマクロの事前知識②「出力の掟」
4日目 Rustマクロの事前知識③「マクロのマナー 衛生性(健全性)」
5日目 Rustマクロの事前知識④ 「聴診器 cargo-expand」
6日目 Rust 宣言マクロ( macro_rules! )の勘所
7日目 Rustマクロ フラグメント指定子
8日目 Rust宣言マクロ 再帰テクニック
9日目 Rust 宣言マクロ小ネタ集【光編① マクロを作る時に便利なマクロ】
10日目 Rust 宣言マクロ小ネタ集【光編② メタ変数式】
11日目 Rust 宣言マクロ小ネタ集【コールバック・マクロの展開結果を別なマクロに渡す方法】
12日目 Rust 宣言マクロ小ネタ集【闇編 宣言マクロ・マッチの限界】
13日目 Rust 宣言マクロのスコープ・インポート・エクスポート
14日目 Rustマクロ フラグメント指定子(ident, expr, item, stmt...)なんもわからん!となったので判別器作った
15日目 Rust 手続きマクロ(proc-macro)の勘所
16日目 Rust 関数風マクロRTA (最小構成の関数風マクロ)
17日目 Rust 属性風マクロRTA (最小構成の属性風マクロ)
18日目 Rust deriveマクロRTA (最小構成のderiveマクロ)
19日目 Rust 関数風マクロを軽くハンズオン
20日目 Rust 属性風マクロを軽くハンズオン
21日目 Rust deriveマクロを軽くハンズオン
22日目 Rust手続きマクロ エラーハンドリング手法
23日目 Rust 手続きマクロ 第四の神器 darling
24日目 【手続きマクロだけではない】synクレートを活用して便利ツールを作った話【Rust】
25日目 Rustマクロ作成チートシート!
おまけ・目次を作成したスクリプト
Rust
#!/usr/bin/env -S cargo +nightly -q -Zscript run --release --manifest-path
---
[dependencies]
glob = "0.3.2"
regex = "1.11.1"
---

fn main() {
    let title_re = regex::Regex::new(r"title: (.*?)\n").unwrap();
    let id_re = regex::Regex::new(r"id: (.*?)\n").unwrap();
    let day_re = regex::Regex::new(r"(\d+)日目").unwrap();

    let mut res_vec = Vec::new();

    for entry in glob::glob("./**/macro_adv_*.md").unwrap() {
        let Ok(path) = entry else {
            println!("Failed to read path");
            continue;
        };

        let content = std::fs::read_to_string(&path).unwrap();

        let Some(title) = title_re.captures(&content).and_then(|cap| cap.get(1)).map(|m| m.as_str()) else {
            println!("Failed to read title: {}", path.display());
            continue;
        };

        let Some(id) = id_re.captures(&content).and_then(|cap| cap.get(1)).map(|m| m.as_str()) else {
            println!("Failed to read id: {}", path.display());
            continue;
        };

        let Some(day): Option<usize> = day_re.captures(&content).and_then(|cap| cap.get(1)).map(|m| m.as_str().parse().unwrap()) else {
            println!("Failed to read day: {}", path.display());
            continue;
        };
        
        let line = format!("| {}日目 |[{}](https://qiita.com/namn1125/items/{})|", day, title, id);

        res_vec.push((day, line));
    }

    res_vec.sort_by_key(|(day, _)| *day);

    for (_, line) in res_vec {
        println!("{}", line);
    }
}

完走した感想

めちゃくちゃ労力をかけた割に得られたのはグッズのみ...もう満足したので記事投稿マラソンへの参加は控えようと思います... :sweat_smile:

とはいえこれは筆者個人の執筆スタイルの問題であり、マラソン自体はとても好きな企画です!あまり手間をかけないようにしつつ大量の情報をきれいにまとめていくという作業機会はそうそう得られるものではないので、マクロについて詳細にまとめられたのはマラソンのおかげだと思います。

もしよければ実際に投稿した記事たちも読んでいただけたら幸いです!

  1. むしろこの制約もRustの好きな点だったりするのですが...

9
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
9
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?