[英]How to delegate pattern matching to a function in Rust?
我有这样的类型:
#[derive(PartialEq, Eq, Debug, Clone)]
enum MyEnum {
ValueOne,
ValueTwo,
Integer(i32),
Text(String),
}
在我的代码中,我有很多类似的模式:
let value = match iterator.next() {
Some(MyEnum::ValueOne) => MyEnum::ValueOne,
Some(value) => return Err(format!("Unexpected value {:?}", value)),
None => return Err("Unexpected end of input!"),
}
或这个:
let value = match iterator.next() {
Some(MyEnum::Integer(i)) => MyEnum::Integer(i),
Some(value) => return Err(format!("Unexpected value {:?}", value)),
None => return Err("Unexpected end of input!"),
}
我想创建一个通用的 function take_value
,我可以在其中指定我需要的MyEnum
类型,它返回Result
我只能用这样的简单值来解决它:
fn take_value(iterator: &mut Iterator<MyEnum>, expected: MyEnum) -> Result<MyEnum, String> {
match iterator.next() {
Some(expected) => Ok(expected),
Some(value) => Err(format!("Unexpected value {:?}", value)),
None => Err("Unexpected end of input!"),
}
}
可以这样调用: let value = take_value(iterator, MyEnum::ValueOne)?;
但是怎么可能修改这个function,让它可以为MyEnum::Integer调用,而不指定里面的integer值呢? 像take_value(iterator, MyEnum::Integer)
你不能用 function 来做,但下面的宏接近你想要的。 因为我们必须区分模式:pat
和表达式:expr
你必须重复那部分。
#[derive(PartialEq, Eq, Debug, Clone)]
enum MyEnum {
ValueOne,
ValueTwo,
Integer(i32),
Text(String),
}
macro_rules! take_value {
($iterator:expr, $pattern:pat, $expr:expr) => {
match $iterator.next() {
Some($pattern) => Ok($expr),
Some(value) => Err(format!("Unexpected value {:?}", value)),
None => Err("Unexpected end of input!".to_string()),
}
}
}
fn main() {
let mut it = [MyEnum::ValueOne, MyEnum::ValueTwo, MyEnum::Integer(5)].into_iter();
dbg!(take_value!(&mut it, MyEnum::ValueOne, MyEnum::ValueOne));
dbg!(take_value!(&mut it, MyEnum::ValueOne, MyEnum::ValueOne));
dbg!(take_value!(&mut it, MyEnum::Integer(i), MyEnum::Integer(i)));
dbg!(take_value!(&mut it, MyEnum::ValueOne, MyEnum::ValueOne));
}
…
src/main.rs:21] take_value!(& mut it, MyEnum :: ValueOne, MyEnum :: ValueOne) = Ok(
ValueOne,
)
[src/main.rs:22] take_value!(& mut it, MyEnum :: ValueOne, MyEnum :: ValueOne) = Err(
"Unexpected value ValueTwo",
)
[src/main.rs:23] take_value!(& mut it, MyEnum :: Integer(i), MyEnum :: Integer(i)) = Ok(
Integer(
5,
),
)
[src/main.rs:24] take_value!(& mut it, MyEnum :: ValueOne, MyEnum :: ValueOne) = Err(
"Unexpected end of input!",
)
你不能用这种“示例值”来做到这一点。 您需要将谓词回调传递给您的take_value
function:
fn take_value<F: Fn(&MyEnum) -> bool>(iterator: &mut dyn Iterator<Item = MyEnum>, predicate: F) -> Result<MyEnum, String> {
match iterator.next() {
Some(value) if predicate (&value) => Ok(value),
Some(value) => Err(format!("Unexpected value {:?}", value)),
None => Err("Unexpected end of input!".to_string()),
}
}
然后你称之为:
let value1 = take_value (&mut it, |v| v == &MyEnum::ValueOne);
let integer = take_value (&mut it, |v| matches!(v, MyEnum::Integer (_)));
或者,您可以隐藏谓词是一个带有简单宏的回调的事实:
macro_rules! mk_pred {
($pat:pat) => {
|v| matches!(v, $pat)
}
}
这允许像这样调用take_value
:
let value1 = take_value (&mut it, mk_pred!(MyEnum::ValueOne));
let integer = take_value (&mut it, mk_pred!(MyEnum::Integer (_)));
我在野外经常看到一种方法,即使它有一些样板,也就是简单地为每个枚举变体创建一个转换 function。 例如:
impl MyEnum {
pub fn as_value_one(&self) -> Result<(), Error> {
match self {
MyEnum::ValueOne => Ok(()),
_ => Err(self.unexpected_value()),
}
}
pub fn as_integer(&self) -> Result<i32, Error> {
match self {
MyEnum::Integer(i) => Ok(*i),
_ => Err(self.unexpected_value()),
}
}
fn unexpected_value(&self) -> Error {
format!("Unexpected value {:?}", self)
}
}
用法:
let value = iterator.next()
.ok_or_else(|| "Unexpected end of input!".to_owned())?
.as_value_one()?;
let value: i32 = iterator.next()
.ok_or_else(|| "Unexpected end of input!".to_owned())?
.as_integer()?;
我将Option
留在了实现之外,因为它似乎不属于这里,但您可以随心所欲地进行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.