【问题标题】:Exhaustivity checking for 1-to-1 mapping between enums枚举之间一对一映射的穷举性检查
【发布时间】:2021-11-16 15:36:30
【问题描述】:

我正在编写一些基本的生物信息学代码来将 DNA 转录为 RNA:

pub enum DnaNucleotide {
    A,
    C,
    G,
    T,
}

pub enum RnaNucleotide {
    A,
    C,
    G,
    U,
}

fn transcribe(base: &DnaNucleotide) -> RnaNucleotide {
    match base {
        DnaNucleotide::A => RnaNucleotide::A,
        DnaNucleotide::C => RnaNucleotide::C,
        DnaNucleotide::G => RnaNucleotide::G,
        DnaNucleotide::T => RnaNucleotide::U,
    }
}

有没有办法让编译器也在match 语句的右侧 一侧进行详尽检查,基本上确保两个枚举之间的1-1 映射?

(一个相关问题:上面可能用某种双射映射更好地表示,但我不想失去穷举检查。有更好的方法吗?)

【问题讨论】:

  • 我知道您想进行一些健全性检查,但最终正确编程是程序员的工作。执行详尽检查的最佳方法是编写该死的单元测试
  • 这不是一个很好的态度。在 Rust 世界中,我们试图避开那种老派的“不要写错误代码,呵呵”的心态。让编译器尽早发现错误是件好事。单元测试通常需要一个重复的逻辑,在两个不同的地方编码相同的需求。
  • 我不知道是否有可能在编译时捕捉到这一点,但这是一个公平的问题。如果答案是“做不到”,我们可以这样说,不会不屑一顾,是吗?

标签: rust enums one-to-one


【解决方案1】:

两个枚举之间存在一一对应的事实表明您实际上应该只在幕后使用一个枚举。 Here is an example 我认为适合您需求的数据模型。这自然是详尽无遗的,因为只有一个枚举开头。

use core::fmt::{Debug, Error, Formatter};

enum NucleicAcid {
    Dna,
    Rna,
}

enum Nucleotide {
    A,
    C,
    G,
    TU,
}

struct BasePair {
    nucleic_acid: NucleicAcid,
    nucleotide: Nucleotide,
}

impl BasePair {
    fn new(nucleic_acid: NucleicAcid, nucleotide: Nucleotide) -> Self {
        Self {
            nucleic_acid,
            nucleotide,
        }
    }
}

impl Debug for BasePair {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        use NucleicAcid::*;
        use Nucleotide::*;

        let BasePair {
            nucleic_acid,
            nucleotide,
        } = self;
        let nucleic_acid_str = match nucleic_acid {
            Dna => "dna",
            Rna => "rna",
        };
        let nucleotide_str = match nucleotide {
            A => "A",
            C => "C",
            G => "G",
            TU => match nucleic_acid {
                Dna => "T",
                Rna => "U",
            },
        };
        f.write_fmt(format_args!("{}:{}", nucleic_acid_str, nucleotide_str))
    }
}

fn main() {
    let bp1 = BasePair::new(NucleicAcid::Dna, Nucleotide::TU);
    let bp2 = BasePair::new(NucleicAcid::Rna, Nucleotide::C);
    
    println!("{:?}, {:?}", bp1, bp2);
    // dna:T, rna:C
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多