メタ変数
#[hooq::method(...)] の ... 内部では特殊な値に置換される メタ変数 を利用することが可能です。メタ変数は $ で始まります。
クイックリファレンス
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$line | usize整数 | フック対象がある行番号 |
$column or $col | usize整数 | フック対象がある列番号 |
$path | 文字列 | フック対象があるファイルの相対パス |
$file | 文字列 | フック対象があるファイルの名前 |
$source | 式 | デバッグ・ロギング用に用いる、挿入/置換対象の式 ( $expr との違いに注意 ) |
$count or $nth | 文字列 | 何番目の置換対象であるかを表示 |
$fn_name or $fnname | 文字列 | フック対象がある関数の名前 |
$fn_sig or $fnsig | 文字列 | フック対象がある関数のシグネチャ |
$xxx (一例) | (任意) | #[hooq::xxx = ...] という不活性属性によるユーザー定義のメタ変数 |
$bindings or $vars | HashMap | メタ変数バインディングすべて |
$hooq_meta or $hooqmeta | hooq::HooqMeta | $line・$col・$path・$file・$source・$count・$bindings をひとまとめにした構造体を表す |
$expr | 式 | 置換用に用いる、置換対象の式 ( $source との違いに注意 ) |
$so_far or $sofar | 式 | 主に挿入用に用いる、これまでに設定されているフック |
$expr、 $so_far は特殊なフックを作るため一旦除くとして、それ以外のすべてのメタ変数を利用した例は以下になります。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::xxx = "user defined binding."]
#[hooq::method(.inspect_err(|_| {
// Fundamental information provided by hooq.
let _line = $line;
let _column = $column;
let _path = $path;
let _file = $file;
let _source = stringify!($source);
let _count = $count;
let _fn_name = $fn_name;
let _fn_sig = $fn_sig;
// Meta vars defined by user.
let _xxx = $xxx;
let _bindings = $bindings;
// All information summarized up to this point.
let _hooq_meta = $hooq_meta;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _line = 28usize;
let _column = 17usize;
let _path = "mdbook-source-code/meta-vars-all/src/main.rs";
let _file = "main.rs";
let _source = "failable(()) ?";
let _count = "1st ?";
let _fn_name = "main";
let _fn_sig = "fn main() -> Result < (), Box < dyn std :: error :: Error > >";
let _xxx = "user defined binding.";
let _bindings = ::std::collections::HashMap::from([
(
::std::string::ToString::to_string("xxx"),
{
let expr = ::std::string::ToString::to_string(
"\"user defined binding.\"",
);
let value: ::std::rc::Rc<dyn ::std::any::Any> = ::std::rc::Rc::new(
"user defined binding.",
);
::hooq::BindingPayload {
expr,
value,
}
},
),
]);
let _hooq_meta = ::hooq::HooqMeta {
line: 28usize,
column: 17usize,
path: "mdbook-source-code/meta-vars-all/src/main.rs",
file: "main.rs",
source_str: "failable(()) ?",
count: "1st ?",
bindings: ::std::collections::HashMap::from([
(
::std::string::ToString::to_string("xxx"),
{
let expr = ::std::string::ToString::to_string(
"\"user defined binding.\"",
);
let value: ::std::rc::Rc<dyn ::std::any::Any> = ::std::rc::Rc::new(
"user defined binding.",
);
::hooq::BindingPayload {
expr,
value,
}
},
),
]),
};
})?;
Ok(())
}
フック対象の情報を得るためのメタ変数
フック対象が存在する行数・列数・関数名・ファイル名・ファイルパス・フック対象のトークン列・何番目の ? であるか等、フック対象の情報を表すメタ変数を紹介します。
line
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$line | usize整数 | フック対象がある行番号 |
フック対象が何行目にあるかを表すusize整数に置き換えられます。
line!() マクロは正確な行数を表示しないため、行数を得たい場合は $line を利用してください。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _line = $line;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _line = 12usize;
})?;
Ok(())
}
column
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$column or $col | usize整数 | フック対象がある列番号 |
フック対象が何列目にあるかを表すusize整数に置き換えられます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _column = $column;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _column = 17usize;
})?;
Ok(())
}
path
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$path | 文字列 | フック対象があるファイルの相対パス |
フック対象が存在するファイルの、クレートルート( CARGO_MANIFEST_DIR )からの相対パスに置き換えられます。
しかしながら相対パスの起点は、ワークスペースを利用している場合などにクレートルートではない時があります。手続きマクロからはファイルの正確な絶対パスが取れない仕組みになっているらしいため、hooqでは絶対パスを取得するためのメタ変数は設けられていません。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _path = $path;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _path = "mdbook-source-code/meta-vars-path/src/main.rs";
})?;
Ok(())
}
file
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$file | 文字列 | フック対象があるファイルの名前 |
フック対象が存在するファイルの名前に置き換えられます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _file = $file;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _file = "main.rs";
})?;
Ok(())
}
source
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$source | 式 | デバッグ・ロギング用に用いる、挿入/置換対象の式 |
hooqマクロによるフックが一切施される前のフック対象トークン列(式)を得られます。デバッグ用途での使用を想定したメタ変数です。
一方で $expr は内部にすでにフックが施された状態の式です。 $expr は置換モード等で元の式を埋め込む場所を決めるために使用されます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _source = stringify!($source);
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _source = "failable(()) ?";
})?;
Ok(())
}
$source メタ変数は単体ではなく以下の文字列化マクロと併用することを想定して作られています。
stringify!マクロhooq::summary!マクロhooq::source_excerpt_helpersモジュール以下にあるマクロ
hooq::summary! マクロを使うと程よく改行が施され、読みやすいエラーメッセージを作成できます。
use hooq::hooq;
#[hooq]
#[hooq::method(.inspect_err(|_| {
let source = ::hooq::summary!($source);
eprintln!("{source}");
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
Err((
"aaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbb",
"cccccccccccccccccccc",
"dddddddddddddddddddd",
"errorerrorerrorerrorerror",
)
.4
.into())
}
実行結果(eprintln! による表示部分):
10> Err((
...
15| "erro..rror",
16| )
17| .4
18| .into())
|
count
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$count or $nth | 文字列 | 何番目の置換対象であるかを表示 |
フック対象がそれぞれの種別( ? か、 return か、末尾式か)で関数内において何番目であるかを指し示します。
本メタ変数は $line の取得がnightlyでしか不可能であった時の開発の名残です。 $line の方が基本的にはわかりやすいでしょう。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _count = $count;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _count = "1st ?";
})?;
Ok(())
}
fn_name
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$fn_name or $fnname | 文字列 | フック対象がある関数の名前 |
フック対象が存在する関数名に置き換わります。ネストされている場合一番内側の関数名を指し、クロージャ内の場合は関数の中のクロージャであることが明記されます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _fn_name = $fn_name;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
(|| -> Result<(), String> {
failable(())?;
Ok(())
})()?;
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 _fn_name = "main";
})?;
(|| -> Result<(), String> {
failable(())
.inspect_err(|_| {
let _fn_name = "__closure_in_main__";
})?;
Ok(())
})()
.inspect_err(|_| {
let _fn_name = "main";
})?;
Ok(())
}
fn_sig
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$fn_sig or $fnsig | 文字列 | フック対象がある関数のシグネチャ |
フック対象が存在する関数・クロージャのシグネチャに置き換わります。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _fn_sig = $fn_sig;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
(|| -> Result<(), String> {
failable(())?;
Ok(())
})()?;
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 _fn_sig = "fn main() -> Result < (), Box < dyn std :: error :: Error > >";
})?;
(|| -> Result<(), String> {
failable(())
.inspect_err(|_| {
let _fn_sig = "| | -> Result < (), String > {}";
})?;
Ok(())
})()
.inspect_err(|_| {
let _fn_sig = "fn main() -> Result < (), Box < dyn std :: error :: Error > >";
})?;
Ok(())
}
ユーザー定義のメタ変数(バインディング)
メタ変数は、組み込み以外の名前ならばユーザーにより定義することが可能です。
#[hooq::xxx = ...] のように不活性属性を用いるか、hooq.tomlファイル内の bindings テーブルに書くことでメタ変数を定義できます。
定義方法の詳細はそれぞれのページを参照してください。
本ページでは不活性属性を用いた例を掲載します。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
enum CauseKind {
DataBase,
Server,
}
#[hooq]
// Can be defined in the format #[hooq::xxx = value]
#[hooq::string = "hello!"] // string literal
#[hooq::integer = 10] // integer literal
#[hooq::cause_kind = CauseKind::Server] // some value
#[hooq::method(.inspect_err(|_| {
let _string = $string;
let _integer = $integer;
let _cause_kind = $cause_kind;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
// Overriding meta variables.
#[hooq::cause_kind = CauseKind::DataBase]
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)
}
enum CauseKind {
DataBase,
Server,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())
.inspect_err(|_| {
let _string = "hello!";
let _integer = 10;
let _cause_kind = CauseKind::Server;
})?;
failable(())
.inspect_err(|_| {
let _string = "hello!";
let _integer = 10;
let _cause_kind = CauseKind::DataBase;
})?;
Ok(())
}
bindings
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$bindings or $vars | HashMap | メタ変数バインディングすべて |
ユーザー定義のメタ変数(バインディング)を HashMap<String, BindingPayload> としてすべて取得します。
BindingPayload にはバインディングの式を文字列化したものと、 Rc<dyn Any> を用いた値が保存されています。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
enum CauseKind {
#[allow(unused)]
DataBase,
Server,
}
#[hooq]
// Can be defined in the format #[hooq::xxx = value]
#[hooq::string = "hello!"] // string literal
#[hooq::integer = 10] // integer literal
#[hooq::cause_kind = CauseKind::Server] // some value
#[hooq::method(.inspect_err(|_| {
let _bindings = $bindings;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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)
}
enum CauseKind {
#[allow(unused)]
DataBase,
Server,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())
.inspect_err(|_| {
let _bindings = ::std::collections::HashMap::from([
(
::std::string::ToString::to_string("cause_kind"),
{
let expr = ::std::string::ToString::to_string(
"CauseKind :: Server",
);
let value: ::std::rc::Rc<dyn ::std::any::Any> = ::std::rc::Rc::new(
CauseKind::Server,
);
::hooq::BindingPayload {
expr,
value,
}
},
),
(
::std::string::ToString::to_string("integer"),
{
let expr = ::std::string::ToString::to_string("10");
let value: ::std::rc::Rc<dyn ::std::any::Any> = ::std::rc::Rc::new(
10,
);
::hooq::BindingPayload {
expr,
value,
}
},
),
(
::std::string::ToString::to_string("string"),
{
let expr = ::std::string::ToString::to_string("\"hello!\"");
let value: ::std::rc::Rc<dyn ::std::any::Any> = ::std::rc::Rc::new(
"hello!",
);
::hooq::BindingPayload {
expr,
value,
}
},
),
]);
})?;
Ok(())
}
hooq_meta
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$hooq_meta or $hooqmeta | hooq::HooqMeta | $line・$col・$path・$file・$source・$count・$bindings をひとまとめにした構造体を表す |
hook フレーバー のために設けられたメタ変数で、メタ変数として得られる情報をhooq::HooqMetaとしてまとめます。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::string = "hello!"]
#[hooq::method(.inspect_err(|_| {
let _hooq_meta = $hooq_meta;
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
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 _hooq_meta = ::hooq::HooqMeta {
line: 13usize,
column: 17usize,
path: "mdbook-source-code/meta-vars-hooq_meta/src/main.rs",
file: "main.rs",
source_str: "failable(()) ?",
count: "1st ?",
bindings: ::std::collections::HashMap::from([
(
::std::string::ToString::to_string("string"),
{
let expr = ::std::string::ToString::to_string("\"hello!\"");
let value: ::std::rc::Rc<dyn ::std::any::Any> = ::std::rc::Rc::new(
"hello!",
);
::hooq::BindingPayload {
expr,
value,
}
},
),
]),
};
})?;
Ok(())
}
hook フレーバーの方も合わせてご確認ください。
高度なフックを作成するためのメタ変数
ここまで紹介したメタ変数は基本的にはロギングやデバッグ等でメタ情報を得るためのものでした。
残りの2つ $expr および $so_far はメソッドの記述を補助するメタ変数になります。
expr
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$expr | 式 | 置換用に用いる、置換対象の式 |
内部に対しhooqマクロによるフックが施された後のフック対象トークン列(式)を得られます。メソッドの置換モード において、フック対象(置換対象)を表すのに用いるメタ変数です。
先に説明した通り $source の方は一切フックが施されていない状態の式です。 $source はデバッグ・ロギング出力で元の式を表示するのに使用します。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
fn wrapper<T, E>(r: Result<T, E>) -> Result<T, E>
where
E: std::fmt::Debug,
{
if let Err(e) = &r {
println!("Error occurred: {:?}", e);
}
r
}
#[hooq]
#[hooq::method(wrapper($expr))]
fn main() -> Result<(), String> {
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 wrapper<T, E>(r: Result<T, E>) -> Result<T, E>
where
E: std::fmt::Debug,
{
if let Err(e) = &r {
{
::std::io::_print(format_args!("Error occurred: {0:?}\n", e));
};
}
r
}
fn main() -> Result<(), String> {
wrapper(failable(()))?;
Ok(())
}
so_far
| 名前 | リテラル種別 | 説明 |
|---|---|---|
$so_far or $sofar | 式 | 主に挿入用に用いる、これまでに設定されているフック |
すでに設定済みのフックを表すメタ変数です。途中で挿入するメソッドを追加したくなった際などに用いることができます。
挿入モード用のフックが入っている場合、意図的に $so_far の中身からは先頭の . (ドット)を抜いてあるので、挿入したい箇所では .$so_far のようにドットを付けて記述します。
use hooq::hooq;
fn failable<T>(val: T) -> Result<T, String> {
Ok(val)
}
#[hooq]
#[hooq::method(.inspect_err(|_| {
let _ = "inserted mode";
}))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
failable(())?;
#[hooq::method(.inspect_err(|_| {
let _ = "before chainned";
}).$so_far)]
failable(())?;
#[hooq::method(.$so_far.inspect_err(|_| {
let _ = "after chainned";
}))]
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 _ = "inserted mode";
})?;
failable(())
.inspect_err(|_| {
let _ = "before chainned";
})
.inspect_err(|_| {
let _ = "inserted mode";
})?;
failable(())
.inspect_err(|_| {
let _ = "inserted mode";
})
.inspect_err(|_| {
let _ = "after chainned";
})?;
Ok(())
}