在Rust中,检查字符串是否以某个字符开头的正确和惯用方法是什么?

42

我想检查一个字符串是否以某些字符开头:

for line in lines_of_text.split("\n").collect::<Vec<_>>().iter() {
    let rendered = match line.char_at(0) {
        '#' => {
            // Heading
            Cyan.paint(*line).to_string()
        }
        '>' => {
            // Quotation
            White.paint(*line).to_string()
        }
        '-' => {
            // Inline list
            Green.paint(*line).to_string()
        }
        '`' => {
            // Code
            White.paint(*line).to_string()
        }
        _ => (*line).to_string(),
    };
    println!("{:?}", rendered);
}

我使用了 char_at,但它因其不稳定性报错。

main.rs:49:29: 49:39 error: use of unstable library feature 'str_char': frequently replaced by the chars() iterator, this method may be removed or possibly renamed in the future; it is normally replaced by chars/char_indices iterators or by getting the first char from a subslice (see issue #27754)
main.rs:49      let rendered = match line.char_at(0) {
                                      ^~~~~~~~~~

我目前正在使用Rust 1.5版本。

1个回答

51

错误消息给出了有用的提示:

该方法经常被 chars() 迭代器替代,可能会在未来被删除或更名;通常可以使用 chars/char_indices 迭代器或从子切片中获取第一个字符来替换它(参见 issue #27754

  1. 我们可以按照错误文本所示:

    for line in lines_of_text.split("\n") {
        match line.chars().next() {
            Some('#') => println!("Heading"),
            Some('>') => println!("Quotation"),
            Some('-') => println!("Inline list"),
            Some('`') => println!("Code"),
            Some(_)   => println!("Other"),
            None      => println!("Empty string"),
        };
    }
    

    请注意,这暴露了您未处理的错误情况!如果没有第一个字符会怎样?

  2. 我们可以对字符串进行切片,然后在字符串切片上进行模式匹配:

  3. for line in lines_of_text.split("\n") {
        match &line[..1] {
            "#" => println!("Heading"),
            ">" => println!("Quotation"),
            "-" => println!("Inline list"),
            "`" => println!("Code"),
            _   => println!("Other")
        };
    }
    

    对字符串进行切片是按字节操作的,因此如果第一个字符不是1个字节(即ASCII字符),则会引发恐慌。如果字符串为空,也会引发恐慌。您可以选择避免这些恐慌:

    for line in lines_of_text.split("\n") {
        match line.get(..1) {
            Some("#") => println!("Heading"),
            Some(">") => println!("Quotation"),
            Some("-") => println!("Inline list"),
            Some("`") => println!("Code"),
            _ => println!("Other"),
        };
    }
    
    我们可以使用直接匹配您问题陈述的方法:str::starts_with
    for line in lines_of_text.split("\n") {
        if line.starts_with('#')      { println!("Heading") }
        else if line.starts_with('>') { println!("Quotation") }
        else if line.starts_with('-') { println!("Inline list") }
        else if line.starts_with('`') { println!("Code") }
        else                          { println!("Other") }
    }
    

    请注意,如果字符串为空或第一个字符不是ASCII,则此解决方案不会出现错误。出于这些原因,我可能会选择这种解决方案。将if体放在与if语句相同的行上并不是正常的Rust风格,但我以这种方式放置它以使其与其他示例保持一致。你应该看看将它们分开放在不同的行上看起来如何。


  4. 另外,你不需要collect ::<Vec <_>>().iter(),这只是低效的。没有理由从迭代器中构建向量,然后对向量进行迭代。只需使用原始迭代器即可。


是的,我也认为收集到向量是低效的。我不知道Split和iter是相同的迭代器。我可以问一下,从你的解决方案中哪个更快?或者它们的速度大致相同? - luthfianto
1
@rilut 你可以问,但我不知道答案 ^_^。也许有人会加入讨论,但你也可以在你的应用程序中进行一些性能测试来确保。我猜它们可能都是一样的。 - Shepmaster
1
每个人都感到困惑,但至少现在答案是完美的 :) - user395760
3
在任何方面,它都不是“相同的”迭代器,但无论如何它仍然是“一个迭代器”。 - bluss
@bluss 是的,那就是我想表达的意思 :) - luthfianto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接