rust 在混合枚举变量的向量上干净地迭代

5q4ezhmt  于 2022-11-24  发布在  其他
关注(0)|答案(2)|浏览(127)

我有如下代码:

enum Either<L, R> {
    Left(L),
    Right(R)
}

enum SomeMessageType {
    // ...
}

fn do_something<T>() {
    let data: Vec<Either<T, SomeMessageType>> = ...;

    // ...
}

我希望能够在不显式指定Either类型的情况下迭代data的内容,因为必须在任何地方指定Either会使API变得丑陋和讨厌。do_something中的泛型类型T将始终是一个枚举变量,我只是不知道是否可以用Rust的泛型类型来表示它。理想情况下,我希望能够写这样的东西:

fn do_something<...>() {
    let data = ...;
    matching_iterator!(data, {
        SomeMessageType::... => ...
        T::SomeVariant => ...
    });
}

我已经试过编写这样的宏:

#[macro_export]
macro_rules! check_mail {
    ( $data:expr, { $( $pattern:pat => $handler:block )+ } ) => {
        {
            use $crate::build_pattern;
                for data in $data.iter() {
                    if let $crate::build_pattern!($( $pattern )+) = message {
                        $( $handler )+
                    }
                }
            }
        }
    };
}

#[macro_export]
macro_rules! build_pattern {
    ( $pattern:pat ) => {
        Either::Right($pattern)
    };

    ( $pattern:pat ) => {
        Either::Left($pattern)
    };
}

但显然这段代码不会编译,更不用说运行了。我的直觉告诉我应该在每个模式的开头放一个区分器,这样更容易编写宏,但我一直无法让它工作。每次尝试都会生成错误的match arm代码,所有的匹配都在开头,然后所有的处理程序都在结尾,我不知道为什么。

zbdgwd5y

zbdgwd5y1#

你必须有一些方法来区分LeftRight模式,我在这里使用了;
此外,你不能匹配一个通用的变量,因为它可能是任何类型,甚至可能没有他们。

fn do_something() {
    use Either::*;
    let data = vec![Right(T::SomeVariant), Left(SomeMessageType::Good)];
    matching_iterator!(data,
        SomeMessageType::Good => {}
        ;
        T::SomeVariant => {}
        T::SomeOtherVariant => {}
    );
}

#[macro_export]
macro_rules! matching_iterator {
    ( $data:expr, $($lpattern:pat => $lhandler:block)+; $($rpattern:pat => $rhandler:block)+ ) => {
        {
            for data in $data.iter() {
                match data {
                    $(Either::Left($lpattern) => $lhandler)+
                    $(Either::Right($rpattern) => $rhandler)+
                }
            }
        }
    };
}
busg9geu

busg9geu2#

我可能是误解了,但你可能会隐藏一个或多个回调?
即:

fn do_something<T, F, H>(f: F, h: H)
    where F: Fn(T) -> (),
          H: Fn(SomeMessageType) -> ()
{
    let data = ...;
    data.for_each(|either| {
        match either {
            Either::Left(t) => f(t),
            Either::Right(r) => h(r),
        }
    })
}

然后,您可以使用这两种类型所需的任何处理程序来调用它:

do_something(
    |t| { match t { MyEnum::Some => ... }}, 
    |msg| { println!("I received this message: {}", msg); }
);

这能解决您的问题吗?因为Either实现隐藏在do_something

相关问题