こんにチュア!本記事は hooqアドベントカレンダー 11日目の記事です!
hooqはメソッドを ? 演算子にフックする属性マクロです。
子供にhooq・hooqって言いたくないですよね?
でも、我が家は大丈夫!...
なんていう茶番1はおいておいて、hooq属性マクロには フックしたい関数全部に #[hooq] を付ける必要がある というデメリットがあります。
use hooq::hooq;
#[hooq] // <- 付けなければならない
fn hoge() -> Result<(), String> {
buddhas_face()?;
Ok(())
}
#[hooq] // <- 付けなければならない
fn fuga() -> Result<(), String> {
buddhas_face()?;
hoge()?;
Ok(())
}
#[hooq] // <- 付けなければならない
fn bar() -> Result<(), String> {
hoge()?;
fuga()?;
Ok(())
}
#[hooq] // <- 付けなければならない
fn main() -> Result<(), String> {
hoge()?;
fuga()?;
bar()?;
Ok(())
}
/// 4回呼ぶと怒られる関数
#[hooq] // <- 付けなければならない
fn buddhas_face() -> Result<(), String> {
use std::sync::{LazyLock, Mutex};
static CALLED_COUNT: LazyLock<Mutex<u32>> = LazyLock::new(|| Mutex::new(0));
let mut counter = CALLED_COUNT.lock().unwrap();
*counter += 1;
if *counter > 3 {
return Err("buddhas_face called more than three times".into());
}
Ok(())
}
しかしこれは将来的に解決される可能性があるかもしれない、ということを本記事に記しておきます!未来の自分へのタイムカプセルですね。
#![feature(custom_inner_attributes)] と #![feature(proc_macro_hygiene)] を付けた場合モジュールには先頭行1回付与で済む
次のようなファイル構成にします。
.
├── Cargo.lock
├── Cargo.toml
├── rust-toolchain.toml
├── src
│ ├── main.rs
│ └── sub.rs
└── target
各種ファイル設定はこんな感じ!
[toolchain]
channel = "nightly"
featureを使うので今のところnightly必須です。
#![feature(custom_inner_attributes)]
#![feature(proc_macro_hygiene)]
mod sub;
use sub::{bar, fuga, hoge};
#[hooq::hooq]
fn main() -> Result<(), String> {
hoge()?;
fuga()?;
bar()?;
Ok(())
}
エントリポイントのある main.rs にて #![feature(...)] を2つ入れます!
-
custom_inner_attributes
- 先頭行の
#![attr]というインナー属性について、ユーザーカスタムである 属性マクロも指定可能にする 機能
- 先頭行の
-
proc_macro_hygiene
- 式や文、インラインでないモジュールなどにも属性をつけられるようにする機能
上記のようにすると、 #[hooq] という記述は モジュールにおいては先頭行だけの付与で済みます!
#![::hooq::hooq] // <- ここだけでよい!
pub fn hoge() -> Result<(), String> {
buddhas_face()?;
Ok(())
}
pub fn fuga() -> Result<(), String> {
buddhas_face()?;
hoge()?;
Ok(())
}
pub fn bar() -> Result<(), String> {
hoge()?;
fuga()?;
Ok(())
}
/// 4回呼ぶと怒られる関数
fn buddhas_face() -> Result<(), String> {
use std::sync::{LazyLock, Mutex};
static CALLED_COUNT: LazyLock<Mutex<u32>> = LazyLock::new(|| Mutex::new(0));
let mut counter = CALLED_COUNT.lock().unwrap();
*counter += 1;
if *counter > 3 {
return Err("buddhas_face called more than three times".into());
}
Ok(())
}
メタ情報は壊れてしまってる...
これで無事属性マクロのファイル先頭付与自体はうまくできますが、hooqの売りである 行数列数のメタ情報 はどうやら壊れてしまう(今回は mod sub; のSpanを指している)ようです...
もし前節のfeatureがstableになった暁には、この時でもうまくメタ情報を取得できる手段を模索する必要がありそうです!
[src/main.rs:4:1] "buddhas_face called more than three times"
4> returnErr("budd..imes".into( ))
|
[src/main.rs:4:1] "buddhas_face called more than three times"
4> buddhas_face( )?
|
[src/main.rs:4:1] "buddhas_face called more than three times"
4> hoge( )?
|
[src/main.rs:14:10] "buddhas_face called more than three times"
14> bar()?
|
Error: "buddhas_face called more than three times"
まとめ・所感
#[tracing::instrument] も都度関数の上にマクロを付けていますし、現状で十分だとは思っているのですが、やはり手抜きエラーロギングのために用意しているマクロなので最終的には RUST_BACKTRACE=1 と同レベルの手軽さになるぐらい記述を削減したいな、という願望より調査した内容でした!
本記事が未来の自分とかの役に立っていれば幸いです。