将类型映射到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!();
}
关于使用这样的特征还有很多要说的。整个系统是图灵完备的,人们在其中构建了很多东西。但这对于这个问答就足够了。