Rustで「let a: impl Trait」のように変数束縛時にimpl Traitするのを実現したいマクロ
やりたいこと
code:rs
let a: impl Debug + Clone = 42;
そのためa.clone()やprintln!("{:?}", a)はできるが、i32に対する処理はできないことが保証できる。
マクロ定義
以下ができたマクロ定義。
code:rs
macro_rules! ty {
( $x:expr, $t:ty ) => {{
fn f(y: $t) -> $t { y }
f($x)
}};
}
関数の戻り値にimpl Traitを指定できることを利用している。
使い方
code:rs
let a = ty!(42, impl Debug + Clone);
// Cloneなので.clone()できる
let b = a.clone();
// Debugなので表示できる
println!("{:?}", a);
IntelliJでRustプラグイン上で見るとわかりやすく薄いグレーで「:impl Debug+Clone」と表示されていることがわかる。 IDEが表示するだけで実際に「:impl Debug+Clone」を記述している訳ではないことに注意。
https://gyazo.com/e0dc27822c00880041b54e277b70b1ed
期待通りにコンパイルエラー
bの実体は42ではあるが、bの型がimpl Debug + Cloneなのでb.abs()をすると以下のように正しくコンパイル時にエラーする。
code:エラー
errorE0599: no method named abs found for type impl std::clone::Clone+std::fmt::Debug in the current scope --> src/main.rs:22:7
|
22 | b.abs();
| ^^^ method not found in impl std::clone::Clone+std::fmt::Debug
error: aborting due to previous error
このマクロは型アノテーションぽいことができるので、impl Trait以外でも何らかの用途があるマクロかもしれない。
hr.icon
追記:型を先に書く方がexprが長いときにも見やすそう。
code:rs
macro_rules! with_type {
( $t:ty, $x:expr ) => {{
fn f(y: $t) -> $t {
y
}
f($x)
}};
}
code:rs
let a = with_type!(impl Debug + Clone, 42);