【问题标题】:How to flatten many nested layers of pattern matches?如何展平许多嵌套的模式匹配层?
【发布时间】:2020-12-24 23:39:22
【问题描述】:

我的代码要求枚举在继续之前是某种类型(如果不是,那么这是一个不可恢复的错误)。用if let 模式表达这一点会导致很多缩进和语法噪音。这是一个例子:

enum Fruit {
    Apple(i32),
    Banana(i32, i32),
}

enum Veggie {
    Tomato(bool),
    Pepper(i32),
}

fn test() -> i32 {
    let fruit = fruit_producing_func();
    if let Fruit::Apple(x) = fruit {
        let veggie = veggie_producing_func(x);
        if let Veggie::Pepper(y) = veggie {
            y
        } else {
            panic!();
        }
    } else {
        panic!();
    }
}

在我的代码中,这种嵌套比 2 层大得多。有没有更简洁的方式来表达这一点,避免创建额外的块,甚至避免重复 panic! 语句?简单地做:

let Fruit::Apple(x) = fruit;

导致错误,因为 let 绑定必须是详尽的(我希望它会在匹配失败时出现恐慌)。

【问题讨论】:

    标签: rust enums pattern-matching flatten


    【解决方案1】:

    您可以改用 match 将其展平:

    fn test() -> i32 {
        let fruit = fruit_producing_func();
        let x = match fruit {
            Fruit::Apple(x) => x,
            _ => panic!(),
        };
        let veggie = veggie_producing_func(x);
        let y = match veggie {
            Veggie::Pepper(y) => y,
            _ => panic!(),
        };
        y
    }
    

    或者,您可以在关联函数中实现匹配,然后使用通常的 Option 惯用语处理该结果。

    impl Fruit {
        fn apple(&self) -> Option<i32> {
            if let Fruit::Apple(x) = self {
                Some(*x)
            } else {
                None
            }
        }
    }
    
    impl Veggie {
        fn pepper(&self) -> Option<i32> {
            if let Veggie::Pepper(y) = self {
                Some(*y)
            } else {
                None
            }
        }
    }
    fn test() -> i32 {
        let fruit = fruit_producing_func();
        let x =  fruit.apple().unwrap();
        let veggie = veggie_producing_func(x);
        veggie.pepper().expect("Tomato is a fruit after all")
    }
    

    【讨论】:

      【解决方案2】:

      您可以将检查重构为枚举本身的便利方法,然后这将使您的 test 函数变平:

      enum Fruit {
          Apple(i32),
          Banana(i32, i32),
      }
      
      impl Fruit {
          // panics if not apple
          fn get_apple(&self) -> i32 {
              if let Fruit::Apple(x) = self {
                  *x
              } else {
                  panic!()
              }
          }
      }
      
      enum Veggie {
          Tomato(bool),
          Pepper(i32),
      }
      
      impl Veggie {
          // panics if not pepper
          fn get_pepper(&self) -> i32 {
              if let Veggie::Pepper(y) = self {
                  *y
              } else {
                  panic!()
              }
          }
      }
      
      fn fruit_producing_func() -> Fruit {
          todo!()
      }
      
      fn veggie_producing_func(x: i32) -> Veggie {
          todo!()
      }
      
      fn test() -> i32 {
          let fruit = fruit_producing_func();
          let x = fruit.get_apple();
          let veggie = veggie_producing_func(x);
          let y = veggie.get_pepper();
          y
      }
      

      playground

      【讨论】:

        猜你喜欢
        • 2019-06-15
        • 2013-02-03
        • 2022-01-11
        • 2021-12-20
        • 2020-03-18
        • 2015-05-08
        • 2018-12-23
        • 1970-01-01
        • 2020-09-13
        相关资源
        最近更新 更多