Rustで「let a: impl Trait」のように変数束縛時にimpl Traitするのを実現したいマクロ
#Rust #impl_Trait #macro
やりたいこと
以下のようなことがしたい。impl_trait_in_bindingsで実現できるがNightly Rust。
code:rs
let a: impl Debug + Clone = 42;
何が嬉しいかというと、「impl_trait_in_bindings - The Rust Unstable Book」でも触れられている通り、aに対してa.abs()などが呼べないことが保証できる。あくまでもDebugとCloneでできることのみを保証する。
そのためa.clone()やprintln!("{:?}", a)はできるが、i32に対する処理はできないことが保証できる。
マクロ定義
以下ができたマクロ定義。
code:rs
macro_rules! ty {
( $x:expr, $t:ty ) => {{
fn f(y: $t) -> $t { y }
f($x)
}};
}
関数の戻り値にimpl Traitを指定できることを利用している。
使い方
以下のように「impl_trait_in_bindings - The Rust Unstable Book」にある例を再現できる。
code:rs
let a = ty!(42, impl Debug + Clone);
// Cloneなので.clone()できる
let b = a.clone();
// Debugなので表示できる
println!("{:?}", a);
Playground
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_export
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);