是否可以编写一个宏来定义一个枚举,该枚举包装任意数量的(不同的)输入类型? 我想做一种类型匹配。
type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"))
这将扩展为:
{
enum Wrapper {
Variant1(i32),
Variant2(f32),
Variant3(Foo),
}
// impl From<i32>, From<f32>, From<Foo> for Wrapper
|x: Wrapper| match x {
Wrapper::Variant1(x) => println!("integer"),
Wrapper::Variant2(x) => println!("float"),
Wrapper::Variant3(x) => println!("foo"),
}
}
这样我就可以写
let switch = type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"));
switch(32.into()); // prints "integer"
switch(24.0.into()); // prints "float"
答案 0 :(得分:1)
按照您的建议编写包装器类型是有意义的,但前提是代码的较大部分中需要包装器类型。
您的特定示例将在每次使用宏时定义一个新的枚举,将值移到新的枚举中,然后立即将其丢弃。
这不是惯用的方法,如果确实是您想象中的用法,我建议您寻找其他选择。
也就是说,我已经多次使用包装器类型。
类似这样的东西对于声明包装器是有用的:
macro_rules! declare_wrapper {
(
$enum_name:ident {
$( $variant_name:ident( $typ:ty : $description:expr ) ),*
}
)=> {
pub enum $enum_name {
$(
$variant_name($typ),
)*
}
$(
impl From<$typ> for $enum_name {
fn from(value: $typ) -> Self {
$enum_name::$variant_name(value)
}
}
)*
impl $enum_name {
fn describe(&self) -> &'static str {
match self {
$(
&$enum_name::$variant_name(_) => $description,
)*
}
}
}
};
}
declare_wrapper!( MyWrapper {
MyInt(i64 : "int"),
MyString(String : "string")
});
fn main() {
let value = MyWrapper::from(22);
println!("{}", value.describe());
}
您还可以扩展它以添加所需的其他方法或特征提示。 我经常做类似的事情。
答案 1 :(得分:1)
在宏中定义一个特征,并为每种类型实现它:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(self);
}
$(
impl TypeMatch for $ty {
fn type_match(self) {
$expr
}
}
)+
TypeMatch::type_match
}}
}
请注意,第一次调用函数时,编译器将绑定该类型,以便后续调用必须具有相同的类型:
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(0);
s(Foo); // Error!
}
如果需要能够用不同的类型调用它,可以通过使用trait对象进行动态调度来解决(花费很少):
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(&self);
}
$(
impl TypeMatch for $ty {
fn type_match(&self) {
$expr
}
}
)+
|value: &dyn TypeMatch| {
value.type_match()
}
}}
}
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(&0);
s(&Foo);
}
还要注意,您必须传递引用而不是值。