【问题标题】:How to check if &str contains enum in Rust?如何检查 &str 是否包含 Rust 中的枚举?
【发布时间】:2025-12-30 00:05:16
【问题描述】:

我在 Rust 中有一个枚举,例如:

enum List {
    Rather,
    Long,
    List,
    Of,
    Items,
}

我有一个 &str,它看起来像:

let a = "InMyList"
let b = "InMyRather"
let c = "NotContain"

有没有一种有效的方式来处理a.contains(List)

我可以遍历每个枚举项并执行a.contains(List::Rather) || a.contains(List::Long) 等。但如果列表足够长,那就是很多样板代码!

【问题讨论】:

  • 并非如此。 &str 可以包含一个枚举变量,但不是必须的。我想遍历每个枚举并检查它是否是 &str 的一部分。
  • 这看起来可能对您有用:strum crate。有了它,您可以遍历变体名称,拼凑一个正则表达式,您可以使用它来扫描字符串。
  • 下面我整理了一个例子,说明如何高效匹配字符串以及将字符串转换为变体等。

标签: rust


【解决方案1】:

如你所说:

我可以遍历每个枚举项并执行 a.contains(List::Rather) || a.contains(List::Long) 等。但是如果列表足够长,那就是很多样板代码!

因此,为了避免键入许多“OR”检查,这不仅乏味而且容易出错,您只需要一种循环遍历所有枚举变体的方法。请查看strum crate 和this 讨论。

注意:如果您的目标是获得最佳性能,那么正则表达式可能是比简单地遍历枚举变体更快的解决方案。请参阅 Todd 的回答和后面的 cmets。

【讨论】:

    【解决方案2】:

    使用正则表达式匹配字符串比遍历每个变体名称并尝试在目标字符串中将其作为子字符串查找更有效。我整理了一个示例,说明如何使用 strumregex 板条箱完成此操作。

    具有一长串变体的枚举,以及一​​组要搜索的目标字符串。

    use std::str::FromStr;
    use lazy_static::lazy_static;
    use regex::Regex;
    use strum::{EnumString, EnumVariantNames, VariantNames};
    
    #[derive(EnumString, EnumVariantNames, Debug)]
    enum Thing {
        Foo,
        Bar,
        Baz,
        Qux,
        Quux,
        Quuz,
        Corge,
        Grault,
        Garply,
        Waldo,
        Fred,
        Plugh,
        Xyzzy,
        Thud,
    }
    
    // Target strings to look for variant names in.
    //
    static STRINGS: [&str; 10] = [
        "fdskQuuzjfkds", "fkjdFoo", "Fred", "fkdXyzzy", "Plughfkdjs",
        "QuuxQuux", "GraultGarply", "TTTThud", "CCorgee", "Waldo",
    ];
    

    将正则表达式设置为只需要在运行时编译一次的静态表达式。它由strum crate 功能提供的变体名称构成。

    lazy_static! {
    
        // A Regular Expression used to find variant names in target strings. 
        //
        static ref THING_EXPR: Regex = {
        
            // Piece together the expression from Thing's variant names.
            let expr_str = Thing::VARIANTS.join("|");
            
            Regex::new(&expr_str).unwrap()  
        };
    }
    

    显示如何迭代字符串集以及检测和处理变体的示例代码。首先捕获变体名称,然后从表达式结果中提取,然后使用字符串检索枚举的同名变体。

    fn main() {
        
        for target in STRINGS {
            if let Some(captures) = THING_EXPR.captures(target) {
            
                // Get the substring that matched one of the variants.
                let variant_name = &captures[0];
                
                // Convert the string to the actual variant.
                let variant = Thing::from_str(variant_name).unwrap();
                
                println!("variant name: {:<8} --  variant: {:?}", 
                         variant_name, variant);
            }                                     
        }
    }
    

    将这些依赖项添加到 Cargo.toml 文件中:

    [dependencies]
    lazy_static = "1.4"
    strum = { version = "0.21", features = ["derive"] }
    regex = "1.5"
    

    输出:

    variant name: Quuz     --  variant: Quuz
    variant name: Foo      --  variant: Foo
    variant name: Fred     --  variant: Fred
    variant name: Xyzzy    --  variant: Xyzzy
    variant name: Plugh    --  variant: Plugh
    variant name: Quux     --  variant: Quux
    variant name: Grault   --  variant: Grault
    variant name: Thud     --  variant: Thud
    variant name: Corge    --  variant: Corge
    variant name: Waldo    --  variant: Waldo
    

    【讨论】:

    • 有些时候正则表达式可能非常有用,但它们并不是灵丹妙药。为什么你说你的解决方案更有效?怎样更有效率?如果您的意思是更快,我相信一个巨大的 union-regex 可能会比字符串循环慢(理论上,trie-regex 可能性能更好,但这是另一回事)。而且就代码复杂性而言,我认为基于循环的解决方案不会比正则表达式更糟糕。
    • @at54321,我对每种方法都进行了 timeit 测试 - code here。使用cargo run --release 运行我看到正则表达式方法比我系统上的循环方法快约 5 倍。调试版本显示相反的结果。
    • 托德,这很有趣。我运行了您的测试,在我的环境中差异较小,但仍然支持正则表达式。然后我用 100 个枚举变体进行了测试,差异变得更大:超过 20 倍。显然,正则表达式做的事情比我预期的要聪明得多。我会编辑我的答案。谢谢!
    • @at54321,这纯粹是推测,但我相信编译的正则表达式相对于.contains() 操作的优势是.contains() 可以扫描整个字符串,或者更多。即使没有足够的字符来满足匹配,它也可能不会放弃,它会为每个变体做到这一点。当正则表达式扫描时,它试图“并行”满足代表变体的每个分支,并知道何时更早停止。
    • 我的测试表明,增加枚举变体的数量会导致基于循环的解决方案大致线性减速(当然这是完全可以理解的),而正则表达式解决方案显示从 10 开始几乎没有减速到 100。这证明正则表达式的效率比.contains() 不是最优的更重要。我想正则表达式被编译为“trie”或类似的东西。
    最近更新 更多