在 Rust 中将一种类型映射到另一种类型
Mapping a type to another type in rust
我正在尝试编写一个 Rust(元)函数,将一些输入类型映射到一些不相关的输出类型。
来自 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);
但是,好吧,这编译失败,因为宏显然 return 不是一个类型。
$ 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 方法是什么?
惯用的方式可能是 type
在 trait
中,例如
trait Matcher {
type Output;
}
impl Matcher for i32 {
type Output = f32;
}
但是,如果要使用 macro_rules!
宏,则需要直接在模式上进行匹配。 match
关键字具有误导性,因为您当前的宏扩展为
match i32 {
i32 => f32,
}
但是 match
需要一个表达式,而不是类型。相反,使用模式的类型:
macro_rules! f {
(i32) => {
f32
};
(i64) => {
f64
};
}
type OutputType = f!(i32);
您的宏不起作用的原因是 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++例子还有很远的距离!宏仅对其输入标记进行操作,这意味着这仅适用于 literal 匹配。例如,这行不通:
fn foo<T>() {
let _: f!(T) = todo!();
}
这会导致“错误:没有规则需要令牌 T
”。
要拥有从一种类型到另一种类型的类型级函数,您想在 Rust 中使用特征。例如:
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(元)函数,将一些输入类型映射到一些不相关的输出类型。
来自 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);
但是,好吧,这编译失败,因为宏显然 return 不是一个类型。
$ 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 方法是什么?
惯用的方式可能是 type
在 trait
中,例如
trait Matcher {
type Output;
}
impl Matcher for i32 {
type Output = f32;
}
但是,如果要使用 macro_rules!
宏,则需要直接在模式上进行匹配。 match
关键字具有误导性,因为您当前的宏扩展为
match i32 {
i32 => f32,
}
但是 match
需要一个表达式,而不是类型。相反,使用模式的类型:
macro_rules! f {
(i32) => {
f32
};
(i64) => {
f64
};
}
type OutputType = f!(i32);
您的宏不起作用的原因是 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++例子还有很远的距离!宏仅对其输入标记进行操作,这意味着这仅适用于 literal 匹配。例如,这行不通:
fn foo<T>() {
let _: f!(T) = todo!();
}
这会导致“错误:没有规则需要令牌 T
”。
要拥有从一种类型到另一种类型的类型级函数,您想在 Rust 中使用特征。例如:
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!();
}
关于使用这样的特征还有很多要说的。整个系统是图灵完备的,人们在其中构建了很多东西。但这应该足以完成此问答。