属性
hooqマクロの挙動は、 #[hooq(ルートメタ)] におけるルートメタおよび、途中に挿入される不活性属性 ( #[hooq::属性(...)] ) によって変更できます。
本ページでは属性による設定可能項目および設定値のデフォルトを解説します。
クイックリファレンス
| 名前 | 種別 | 説明 |
|---|---|---|
| flavor | マクロルートのメタ | 指定したフレーバーの設定を適用する |
| trait_use | マクロルートのメタ | 指定したパス( XXX )について use XXX as _; をアイテムの前に挿入する |
| method | 不活性属性 | 挿入/置換するメソッド(置換の場合は式)を設定する |
| skip_all / skip | 不活性属性 | 本属性を付与した式へのフックは行わないようになる |
| hook_targets | 不活性属性 | ?, return, 末尾式(tail_expr)それぞれについてフックを行うかを切り替え(デフォルトは3種すべてにフック) |
| tail_expr_idents | 不活性属性 | 末尾式に来た時にフックを行うidentを指定(デフォルトでは Err ) |
| ignore_tail_expr_idents | 不活性属性 | フック対象であった場合でもフックを行わないidentを指定(デフォルトでは Ok ) |
| result_types | 不活性属性 | return と末尾式にフックを行う関数の返り値型を指定(デフォルトは Result ) |
| hook_in_macros | 不活性属性 | マクロ内にもフックを行うかを指定(デフォルトは true ) |
| binding | 不活性属性 | 指定したリテラル・式で置換されるメタ変数を作成 |
- マクロルートのメタ: 属性マクロ本体
#[hooq(...)]の...部分に指定する属性(メタ) - 不活性属性: 属性マクロ本体(
#[hooq],#[hooq(...)])ではなく、その後に来るアイテムの随所に付与される属性。#[hooq::属性(...)]のようなフォーマットで指定
すべての属性を付与してみたソースコードは以下のような感じです。
use hooq::hooq;
mod sub {
pub trait Trait {}
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq(flavor = "hook", trait_use(sub::Trait))] // Attribute macro root.
#[hooq::method(.inspect_err(|_| { let _ = "error!"; }))] // All following attributes are inert.
#[hooq::hook_targets("?", "return", "tail_expr")]
#[hooq::tail_expr_idents("Err")]
#[hooq::ignore_tail_expr_idents("Ok")]
#[hooq::result_types("Result")]
#[hooq::hook_in_macros(true)]
#[hooq::binding(xxx = "xxx_value")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
#[hooq::skip_all]
if failable(false)? {
failable(())?;
}
#[hooq::skip]
if failable(false)? {
// Next line is not skipped.
failable(())?;
}
#[hooq::method(.inspect_err(|_| { let _ = $xxx; }))]
failable(())?;
Ok(())
}
上記のソースコードでマクロが展開されると次のようになります。
use hooq::hooq;
mod sub {
pub trait Trait {}
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[allow(unused)]
use sub::Trait as _;
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())
.inspect_err(|_| {
let _ = "error!";
})?;
if failable(false)? {
failable(())?;
}
if failable(false)? {
failable(())
.inspect_err(|_| {
let _ = "error!";
})?;
}
failable(())
.inspect_err(|_| {
let _ = "xxx_value";
})?;
Ok(())
}
ルートメタ
hooq属性マクロ( #[hooq] )をアイテムに付与する際に、メタ部分で指定できる事項が2つ存在します。
| 名前 | 付与方法 |
|---|---|
| flavor | #[hooq(FLAVOR_NAME)] or #[hooq(flavor = "FLAVOR_NAME")] |
| trait_use | #[hooq(trait_use(TRAIT_PATH, ...))] or #[hooq(trait_uses(TRAIT_PATH, ...))] |
解説は各項でします。
flavor
#[hooq(FLAVOR_NAME)] または #[hooq(flavor = "FLAVOR_NAME")] というフォーマットでベースとなる設定をフレーバーから設定します。
use hooq::hooq;
#[hooq(my_flavor)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
func()?;
Ok(())
}
#[hooq(my_flavor::sub_flavor)]
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq(flavor = "my_flavor.sub_flavor")]
fn func() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
Ok(())
}
hooq.toml の内容は以下であるとします。
[my_flavor]
method = """.inspect(|_| {
let _ = $tag;
})"""
bindings = { tag = "\"my_flavor\"" }
[my_flavor.sub_flavor]
bindings = { tag = "\"my_flavor.sub_flavor\"" }
tail_expr_idents = ["Ok"]
この時フレーバーの設定が読まれ展開後は以下のようになります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn main() -> Result<(), Box<dyn std::error::Error>> {
func()
.inspect(|_| {
let _ = "my_flavor";
})?;
Ok(())
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
.inspect(|_| {
let _ = "my_flavor.sub_flavor";
})
}
fn func() -> Result<(), Box<dyn std::error::Error>> {
failable(())
.inspect(|_| {
let _ = "my_flavor.sub_flavor";
})?;
Ok(())
.inspect(|_| {
let _ = "my_flavor.sub_flavor";
})
}
例ではサブフレーバーも利用しています。サブフレーバーは、元となるフレーバーに上書きで設定を施したフレーバーです。
サブフレーバーは文字列の場合 . か ::、パスのような形で直接渡す場合 :: で区切られた名前空間で表し、ネストさせる(つまり、サブフレーバーにさらにサブフレーバーを設ける)ことができます。
フレーバーのさらなる情報、特にanyhowなどのhooqが予め用意しているフレーバーや、hooq.tomlへの書き方等についてはフレーバーのページを参照してください。
trait_use
#[hooq(trait_use(トレイトパス, ...))] というフォーマットで指定します。 trait_use ではなく trait_uses でも構いません1。この時指定されたトレイトパスに対して #[allow(unused)] use トレイトパス as _; というuse文が、 #[hooq(...)] を付与したアイテムの前に出力されます。
use hooq::hooq;
mod sub {
pub trait Inserted {
fn inserted(self) -> Self;
}
impl<T, E> Inserted for Result<T, E> {
fn inserted(self) -> Self {
self
}
}
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq(trait_use(sub::Inserted))]
#[hooq::method(.inserted())]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
Ok(())
}
展開後の main 関数とその上に出力されるuse文を抜粋すると次のようになっています。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
mod sub {
pub trait Inserted {
fn inserted(self) -> Self;
}
impl<T, E> Inserted for Result<T, E> {
fn inserted(self) -> Self {
self
}
}
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[allow(unused)]
use sub::Inserted as _;
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(()).inserted()?;
Ok(())
}
例で示したように、 trait_use はフックするメソッドを呼び出すのに必要なトレイトを一緒に出力する目的で設けています。もちろんこの属性指定を利用せず直接use文を書いてしまっても構いませんが、例えば #[cfg_attr(test, hooq(flavor = "test", trait_use(Trait)))] のようにコンパイル条件付きでhooqの利用をしたい場合などに、記述を多少見やすくする効果が期待できます。
あるいは、hooq.tomlでも trait_use は(trait_uses フィールドで)指定できるため、 システム上一応属性でも設定可能にしたとも言えます。例えばanyhowフィーチャーでは with_context メソッドを挿入しますが、この時同時に必要なトレイトである anyhow::Context をuse文で導入しています2。
method
| 名前 | 付与方法 |
|---|---|
| method | #[hooq::method(...)] |
#[hooq::method(.method_name())] のようなフォーマットで、フックするメソッドを設定できる不活性属性です。
use hooq::hooq;
#[hooq]
#[hooq::method(.inspect_err(|_| { let _ = "specified @ root"; }))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
#[hooq::method(.inspect_err(|_| { let _ = "specified @ inner"; }))]
failable(())?;
Ok(())
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
上の例のように、「 #[hooq] マクロのすぐ下」・「関数内部」のどちらでも設定の変更を行うことができます。これは以降紹介する不活性属性で共通の性質です。 main 関数は以下のように展開されます。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())
.inspect_err(|_| {
let _ = "specified @ root";
})?;
failable(())
.inspect_err(|_| {
let _ = "specified @ inner";
})?;
Ok(())
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq::method(...)] には 挿入モード と 置換モード の2つのモードがあります。
.(ドット)で始まる場合は挿入モードです。対象の式と?の間(あるいはreturnや末尾式なら単に式の末尾)にメソッドを挿入します。- 上記以外は置換モードになり、与えられた式によって対象の式を置換します。対象の式は
$exprメタ変数で得られるので、例えばfn func<T, E>(_r: Result<T, E>) {}などに対して#[hooq::method(func($expr))]のように書くことで関数で対象式をラップするといった記述が可能です。
その他、フックするメソッド内では $line や $source といった メタ変数 を利用することが可能です。
ユーザーが特に何も指定しない場合、defaultフレーバーの設定値である以下のメソッドが挿入されます。
.inspect_err(|e| {
let path = $path;
let line = $line;
let col = $col;
let expr = ::hooq::summary!($source);
::std::eprintln!("[{path}:{line}:{col}] {e:?}\n{expr}");
})
モードやメタ変数に関する詳細はメソッドとメタ変数のページを参照してください。
フックの付与をスキップする
| 名前 | 付与方法 |
|---|---|
| skip_all | #[hooq::skip_all] |
| skip | #[hooq::skip] |
skip_all
hooqは(デフォルトの設定では)かなり貪欲にメソッドを ? 等にフックしてきます。「この ? にはフックしたくない!」といった要望は自然に起きると思います。そのような場合はフック付与を行いたくない式・文に #[hooq::skip_all] を付けてください。
use hooq::hooq;
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
#[hooq::skip_all]
let f = || -> Option<()> {
optional(())?; // If the hook is applied, an compile error occurs.
Some(())
};
let _ = failable(f())?;
Ok(())
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn optional<T>(val: T) -> Option<T> {
Some(val)
}
付与した式とその内部にはフックされなくなります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(()).inspect_err(|_| {})?;
let f = || -> Option<()> {
optional(())?;
Some(())
};
let _ = failable(f()).inspect_err(|_| {})?;
Ok(())
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn optional<T>(val: T) -> Option<T> {
Some(val)
}
skip
#[hooq::skip] は滅多に利用することはないであろう不活性属性になりますが、一応用意した #[hooq::skip_all] の亜種になります。
skip_allでは付与対象全体でフックがスキップされましたが、skipでは 付与した(親)スコープでのみ フックのスキップが起きます。
skipは、末尾式がネストしてしまっており、そのままロギングをフックするとログが見辛くなってしまう場合等に有用です。
use hooq::hooq;
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn func1() -> Result<(), String> {
match failable(failable(()))? {
Ok(()) => Ok(()),
Err(s) => Err(s),
}
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn func2() -> Result<(), String> {
#[hooq::skip]
match failable(failable(()))? {
Ok(()) => Ok(()),
Err(s) => Err(s),
} // Not hooked here.
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn main() {
let _ = func1();
let _ = func2();
}
#[hooq::skip] がついている func2 の方ではmatch式の外につく inspect_err がなくなっていることがわかります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn func1() -> Result<(), String> {
match failable(failable(())).inspect_err(|_| {})? {
Ok(()) => Ok(()),
Err(s) => Err(s).inspect_err(|_| {}),
}
.inspect_err(|_| {})
}
fn func2() -> Result<(), String> {
match failable(failable(()))? {
Ok(()) => Ok(()),
Err(s) => Err(s).inspect_err(|_| {}),
}
}
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn main() {
let _ = func1();
let _ = func2();
}
フック対象判定を制御する
以下は各式に対して、フックするかしないかをカスタマイズするための不活性属性群になります。
| 名前 | 付与方法 | ... の部分が取りうる値 |
|---|---|---|
| hook_targets | #[hooq::hook_targets(...)] | ? or return or tail_expr の中から複数 |
| tail_expr_idents | #[hooq::tail_expr_idents(...)] | Err など |
| ignore_tail_expr_idents | #[hooq::ignore_tail_expr_idents(...)] | Ok など |
| result_types | #[hooq::result_types(...)] | Result など |
| hook_in_macros | #[hooq::hook_in_macros(...)] | true or false |
設定値詳細は各項目にて示します。ここには、設定の適用優先順位を記します3。
skip_allが付与されている場合はフックしないskipの場合は子スコープを除いた同スコープ内についてのみフックしない
- 対象式がマクロ呼び出し内部にあり、かつ
hook_in_macrosがfalseである場合はフックしない ?へのフックの場合hook_targetsに?が含まれていればフックする
returnへのフックの場合hook_targetsにreturnが含まれていなければフックしない- 返り値の識別子が
tail_expr_identsに含まれていればフックする - 関数の返り値型が
result_typesに含まれ、かつ返り値の識別子がignore_tail_expr_identsに含まれない場合フックする
- 末尾式へのフックの場合
hook_targetsにtail_exprが含まれていなければフックしない- 末尾式の識別子が
tail_expr_identsに含まれていればフックする - 関数・クロージャが持つブロックの末尾式であり、かつその関数・クロージャの返り値型が
result_typesに含まれ、かつ返り値の識別子がignore_tail_expr_identsに含まれない場合フックする
hook_targets
? 演算子(Question Operator)、 return、 末尾式( tail_expr )の3つについて、それぞれフックするかしないかを指定できます。デフォルトでは3種類すべてフックされます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
#[hooq::hook_targets("?")]
fn target_question() -> Result<(), String> {
failable(())?;
if failable(false)? {
return Err("error".into());
}
if failable(true)? {
Ok(())
} else {
Err("error".into())
}
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
#[hooq::hook_targets("return")]
fn target_return() -> Result<(), String> {
failable(())?;
if failable(false)? {
return Err("error".into());
}
if failable(true)? {
Ok(())
} else {
Err("error".into())
}
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
#[hooq::hook_targets("tail_expr")]
fn target_tail_expr() -> Result<(), String> {
failable(())?;
if failable(false)? {
return Err("error".into());
}
if failable(true)? {
Ok(())
} else {
Err("error".into())
}
}
fn main() {
let _ = target_question();
let _ = target_return();
let _ = target_tail_expr();
}
展開結果は次の通りとなります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn target_question() -> Result<(), String> {
failable(()).inspect_err(|_| {})?;
if failable(false).inspect_err(|_| {})? {
return Err("error".into());
}
if failable(true).inspect_err(|_| {})? { Ok(()) } else { Err("error".into()) }
}
fn target_return() -> Result<(), String> {
failable(())?;
if failable(false)? {
return Err("error".into()).inspect_err(|_| {});
}
if failable(true)? { Ok(()) } else { Err("error".into()) }
}
fn target_tail_expr() -> Result<(), String> {
failable(())?;
if failable(false)? {
return Err("error".into());
}
if failable(true)? { Ok(()) } else { Err("error".into()).inspect_err(|_| {}) }
.inspect_err(|_| {})
}
fn main() {
let _ = target_question();
let _ = target_return();
let _ = target_tail_expr();
}
今回は個別に書きましたが、 #[hooq::hook_targets("?", "return")] のように一つだけ抜く書き方等ももちろんできます。
tail_expr_idents
return の返り値あるいは末尾式が指定した識別子である際は、関数の返り値型が result_types に含まれるかに関わらずフックを行います。デフォルトは Err で、カンマ区切りで複数指定可能です。
識別子にはパス( xxx::yyy::Zzz )は認められず、単体( Zzz )である必要があります。
一致に関しては、パス中で最後の識別子(xxx::yyy::Zzz なら Zzz)で判別されます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn main() -> Result<(), String> {
let _: Result<(), String> = {
let _: Result<(), String> = {
let res = "error".to_string();
Err(res)
};
failable(())
};
#[hooq::tail_expr_idents("Err", "failable")]
let _: Result<(), String> = {
let _: Result<(), String> = {
let res = "error".to_string();
Err(res)
};
failable(()) // This will be hooked because of tail_expr_idents.
};
Ok(())
}
tail_expr_idents のおかげで、ブロックの返り値などに Err が含まれる場合、そこにもフックが行われるようになります。 Err 以外にこのような性質を持たせたい識別子がある際は tail_expr_idents に加えることで同様の挙動になります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn main() -> Result<(), String> {
let _: Result<(), String> = {
let _: Result<(), String> = {
let res = "error".to_string();
Err(res).inspect_err(|_| {})
};
failable(())
};
let _: Result<(), String> = {
let _: Result<(), String> = {
let res = "error".to_string();
Err(res).inspect_err(|_| {})
};
failable(()).inspect_err(|_| {})
};
Ok(())
}
ignore_tail_expr_idents
tail_expr_idents とは対照的に、 return の返り値あるいは末尾式が指定した識別である際は関数の返り値型が result_types に含まれている場合でも フックを行いません 。デフォルトは Ok で、カンマ区切りで複数指定可能です。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn main() {
let f = || -> Result<(), String> { failable(()) };
#[hooq::ignore_tail_expr_idents("failable")]
let g = || -> Result<(), String> { failable(()) };
#[hooq::tail_expr_idents("!failable")]
let h = || -> Result<(), String> { failable(()) };
f().unwrap();
g().unwrap();
h().unwrap();
}
上記例にあるように、 ignore_tail_expr_idents を利用せずとも、 tail_expr_idents への指定において頭に ! (Exclamation Mark) を付けることでも同様の設定を行うことが可能です。展開結果は以下の通りとなります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn main() {
let f = || -> Result<(), String> { failable(()).inspect_err(|_| {}) };
let g = || -> Result<(), String> { failable(()) };
let h = || -> Result<(), String> { failable(()) };
f().unwrap();
g().unwrap();
h().unwrap();
}
同じ識別子が tail_expr_idents と ignore_tail_expr_idents の両方に含まれる場合、機構が単純なため フックされてしまいます。なるべく ! (Exclamation Mark) を利用して tail_expr_idents 経由で設定した方が確実です。
Errの例
Err はデフォルトで tail_expr_idents に含まれるので、 ignore_tail_expr_idents で指定してもフックされてしまいます。
use hooq::hooq;
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn main() {
let f = || -> Result<(), String> { Err("error!".to_string()) };
#[hooq::ignore_tail_expr_idents("Err")]
let g = || -> Result<(), String> { Err("error!".to_string()) };
#[hooq::tail_expr_idents("!Err")]
let h = || -> Result<(), String> { Err("error!".to_string()) };
f().unwrap_err();
g().unwrap_err();
h().unwrap_err();
}
クロージャ g ではフックしてほしくないのにフックされていることが確認できます。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn main() {
let f = || -> Result<(), String> { Err("error!".to_string()).inspect_err(|_| {}) };
let g = || -> Result<(), String> { Err("error!".to_string()).inspect_err(|_| {}) };
let h = || -> Result<(), String> { Err("error!".to_string()) };
f().unwrap_err();
g().unwrap_err();
h().unwrap_err();
}
result_types
hooqマクロを付与した関数の返り値がフック対象であるかを判別するための識別子を設定する不活性属性です。関数の返り値型の識別子が result_types で指定した識別子と一致する時、 return や末尾式でフックを行うようになります。デフォルトは Result で、カンマ区切りで複数指定可能です。
tail_expr_idents等と同様に、識別子にはパス( xxx::yyy::Zzz )は認められず、単体( Zzz )である必要があります。
一致に関しては、パス中で最後の識別子(xxx::yyy::Zzz なら Zzz)で判別されます。
Result 型以外に独自で別な型を扱う際などに有用です。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
type MyResult = Result<(), String>;
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn func1() -> MyResult {
let _ = || -> Result<(), String> { failable(()) };
failable(())
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
#[hooq::result_types("MyResult")]
fn func2() -> MyResult {
// No longer hooked.
let _ = || -> Result<(), String> { failable(()) };
failable(())
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
#[hooq::result_types("Result", "MyResult")]
fn func3() -> MyResult {
let _ = || -> Result<(), String> { failable(()) };
let _ = || {
// Not hooked because return type of the closure is unknown.
failable(())
};
failable(())
}
fn main() {
let _ = func1();
let _ = func2();
let _ = func3();
}
展開結果は次のようになります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
type MyResult = Result<(), String>;
fn func1() -> MyResult {
let _ = || -> Result<(), String> { failable(()).inspect_err(|_| {}) };
failable(())
}
fn func2() -> MyResult {
let _ = || -> Result<(), String> { failable(()) };
failable(()).inspect_err(|_| {})
}
fn func3() -> MyResult {
let _ = || -> Result<(), String> { failable(()).inspect_err(|_| {}) };
let _ = || { failable(()) };
failable(()).inspect_err(|_| {})
}
fn main() {
let _ = func1();
let _ = func2();
let _ = func3();
}
hook_in_macros
関数風マクロ呼び出し内に存在する対象の式に対し、フックを行うかを決定します。デフォルトは true でありマクロ呼び出し内までフックが行われます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("{}", failable("hello")?);
#[hooq::hook_in_macros(false)]
println!("{}", failable("world")?);
Ok(())
}
展開結果は次の通りです。 println! マクロも展開されている点に注意してください。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
{
::std::io::_print(format_args!("{0}\n", failable("hello").inspect_err(|_| {})?));
};
{
::std::io::_print(format_args!("{0}\n", failable("world")?));
};
Ok(())
}
関数風マクロの引数部分はRustの文法に従った構文になっているとは限らず、フックを行う場合に解析に少しコストがかかるためオフにできる不活性属性を設けました。マクロの内側までフックする必要がない場合はこちらの設定を false にすることで多少コンパイル時間が短くなるかもしれません(多分)。
binding
メタ変数 において、ユーザーが自由に式を保存できる バインディング 機能があります。同じ意味を表すいくつかの書き方があります。
| 付与方法 | 備考 |
|---|---|
#[hooq::binding(xxx = ...)] | |
#[hooq::var(xxx = ...)] | |
#[hooq::xxx = ...] | この方法で記述する場合 xxx は他不活性属性と名前衝突不可 |
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| { let _ = $xxx; }))]
#[hooq::xxx = 10]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
#[hooq::binding(xxx = "in block")]
{
failable(())?;
#[hooq::var(xxx = 42)]
failable(())?;
failable(())?;
}
failable(())?;
Ok(())
}
展開結果は次のようになります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())
.inspect_err(|_| {
let _ = 10;
})?;
{
failable(())
.inspect_err(|_| {
let _ = "in block";
})?;
failable(())
.inspect_err(|_| {
let _ = 42;
})?;
failable(())
.inspect_err(|_| {
let _ = "in block";
})?;
}
failable(())
.inspect_err(|_| {
let _ = 10;
})?;
Ok(())
}
バインディング については当該ページの方も参照してください。
flavor を利用した設定の部分適用
ここまで紹介してきた不活性属性による設定は、フレーバーを用いた部分適用が可能です。部分適用は #[hooq::属性 = フレーバー名] で行います。
また、すべての設定を不活性属性で上書きしたい場合は #[hooq::flavor = フレーバー名]、存在するユーザー定義のバインディングを上書きしたい場合は #[hooq::bindings = フレーバー名] という記法が使えます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
type MyResult = Result<(), String>;
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _x = $xxx;
let _y = $yyy;
}))]
#[hooq::xxx = "from root"]
#[hooq::yyy = "from root"]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
// Not hooked.
let _ = || -> MyResult { failable(()) };
#[hooq::method = "my_flavor"]
// Method will be changed.
failable(())?;
#[hooq::result_types = "my_flavor"]
// Hooked now.
let _ = || -> MyResult { failable(()) };
#[hooq::bindings = "my_flavor"]
// Bindings will be changed.
failable(())?;
#[hooq::flavor = "my_flavor"]
// All will be changed.
failable(())?;
Ok(())
}
hooq.tomlの内容は以下である時、
[my_flavor]
method = """.inspect_err(|_| {
let _ = "from my_flavor";
let _x = $xxx;
let _y = $yyy;
})"""
result_types = ["Result", "MyResult"]
bindings = { xxx = "\"xxx from my_flavor\"", yyy = "\"yyy from my_flavor\"" }
展開結果は次のようになります。
#![feature(prelude_import)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
type MyResult = Result<(), String>;
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())
.inspect_err(|_| {
let _x = "from root";
let _y = "from root";
})?;
let _ = || -> MyResult { failable(()) };
failable(())
.inspect_err(|_| {
let _ = "from my_flavor";
let _x = "from root";
let _y = "from root";
})?;
let _ = || -> MyResult {
failable(())
.inspect_err(|_| {
let _x = "from root";
let _y = "from root";
})
};
failable(())
.inspect_err(|_| {
let _x = "xxx from my_flavor";
let _y = "yyy from my_flavor";
})?;
failable(())
.inspect_err(|_| {
let _ = "from my_flavor";
let _x = "xxx from my_flavor";
let _y = "yyy from my_flavor";
})?;
Ok(())
}