web-dev-qa-db-ja.com

Rustマクロの式のタイプを一致させるにはどうすればよいですか?

これは単なる擬似コードです。

macro_rules! attribute {
    $e: expr<f32> => { /* magical float stuff */ };
    $e: expr<i64> => { /* mystical int stuff */ };
};

マクロに渡したタイプに応じて、異なる展開のマクロが欲しいのですが。

これはC++でどのように機能するかです

template <typename T>
struct Attribute{ void operator(T)() {} };

template <>
struct Attribute<float> {
    void operator(float)(float) { /* magical float stuff */ }
};

template <>
struct Attribute<long> {
    void operator()(long) { /* mystical int stuff */ }
}
18
Arne

Rustマクロはそれを行うことができません。マクロは、セマンティックレベルではなく、構文レベルで動作します。つまり、コンパイラは式(構文)があることを認識していても、マクロが展開された時点で式の値のタイプ(セマンティック)が何であるかを認識していません。

回避策は、予期される型をマクロに渡すことです。

_macro_rules! attribute {
    ($e:expr, f32) => { /* magical float stuff */ };
    ($e:expr, i64) => { /* mystical int stuff */ };
}

fn main() {
    attribute!(2 + 2, i64);
}
_

または、もっと簡単に言えば、複数のマクロを定義します。


式のタイプに基づいて静的(コンパイル時)ディスパッチを実行する場合は、トレイトを使用できます。必要なメソッドを使用してトレイトを定義してから、必要なタイプのトレイトを実装します。 implブロックがトレイト定義と同じクレートにある場合は、anyタイプ(他のライブラリのプリミティブとタイプを含む)のトレイトを実装できます。

_trait Attribute {
    fn process(&self);
}

impl Attribute for f32 {
    fn process(&self) { /* TODO */ }
}

impl Attribute for i64 {
    fn process(&self) { /* TODO */ }
}

macro_rules! attribute {
    ($e:expr) => { Attribute::process(&$e) };
}

fn main() {
    attribute!(2 + 2);
}
_

注:マクロの本体に$e.process()を書き込むこともできますが、その場合、マクロは無関係のprocessメソッドを呼び出す可能性があります。

22
Francis Gagné

すでに説明したように、exprのタイプに応じて異なる方法で展開することはできません。ただし、回避策として、 any module を使用して、Anyトレイトからダウンキャストしてみることができます。

use std::any::Any;

macro_rules! attribute {
    ( $e:expr ) => {
        if let Some(f) = (&$e as &Any).downcast_ref::<f32>() {
            println!("`{}` is f32.", f);
        } else if let Some(f) = (&$e as &Any).downcast_ref::<f64>() {
            println!("`{}` is f64.", f);
        } else {
            println!("I dunno what is `{:?}` :(", $e);
        }
    };
}

fn main() {
    attribute!(0f32);
    attribute!(0f64);
    attribute!(0);
}

表示:

`0` is f32.
`0` is f64.
I dunno what is `0` :(
4
Boiethios