将类型映射到Rust中的另一种类型

我正在尝试编写一个 rust (meta-) 函数,将一些输入类型映射到一些不相关的输出类型。

来自 C++ 背景,我通常会这样写:

template<typename T>
struct f;

template<>
struct f<int> { using type = double; };

using input_type = int;
using output_type = f<input_type>::type;

我天真地尝试在 Rust 中编写相同的内容如下:

macro_rules! f {
  ($in: ty) => {
    match $in {
      i32 => f32,
    }
  }
}

type OutputType = f!(i32);

但是,好吧,这无法编译,因为宏显然没有返回类型。

$ rustc typedef.rs 
error: expected type, found keyword `match`
 --> typedef.rs:3:5
  |
3 |     match $in {
  |     ^^^^^ expected type
...
9 | type OutputType = f!(i32);
  |                   -------
  |                   |
  |                   this macro call doesn't expand to a type
  |                   in this macro invocation
  |

将一种类型映射到另一种类型的惯用 Rust 方式是什么?

回答

您的宏不起作用的原因是match. 您的代码type OutputType = f!(i32);将扩展为:

type OutputType = match i32 {
    i32 => f32,
};

但你不能match过度类型。match仅适用于值。但是,macro_rules!它本身已经具有对令牌进行操作的模式匹配功能。所以你可以这样写宏:

macro_rules! f {
  (i32) => { f32 };
  (i64) => { f64 };
}

type OutputType = f!(i32);

但这与您的 C++ 示例相距甚远!宏只对其输入标记进行操作,这意味着这仅适用于文字匹配。例如,这不起作用:

fn foo<T>() {
    let _: f!(T) = todo!();
}

这导致“错误:没有规则预期令牌T”。

要从一种类型到另一种类型的类型级函数,您需要在 Rust 中使用 trait。例如:

trait F {
    type Out;
}

impl F for i32 {
    type Out = f32;
}

impl F for i64 {
    type Out = f64;
}

type OutputType = <i32 as F>::Out;

fn foo<T>() {
    // This works now.
    let _: <T as F>::Out = todo!();
}

关于使用这样的特征还有很多要说的。整个系统是图灵完备的,人们在其中构建了很多东西。但这对于这个问答就足够了。


以上是将类型映射到Rust中的另一种类型的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>