为什么我可以在if语句中将一个String与&str进行比较,但是在使用match时却不行?

53

我正在尝试实现一个函数,该函数读取命令行参数并将它们与硬编码的字符串文字进行比较。

当我使用一个if语句进行比较时,它完美地工作:

fn main() {
    let s = String::from("holla!");

    if s == "holla!" {
        println!("it worked!");
    }
}

但是使用match语句(我想这会更优雅):

fn main() {
    let s = String::from("holla!");

    match s {
        "holla!" => println!("it worked!"),
        _ => println!("nothing"),
    }
}

编译器一直报错,说期望一个String,但发现了一个&static str

error[E0308]: mismatched types
 --> src/main.rs:5:9
  |
5 |         "holla!" => println!("it worked!"),
  |         ^^^^^^^^ expected struct `std::string::String`, found reference
  |
  = note: expected type `std::string::String`
             found type `&'static str`

我看过如何在Rust中将字符串与字符串字面值匹配?,所以我知道如何修复它,但我想知道为什么在使用if时比较可以工作,但不使用match


因为 &str 是一个引用,而不是一个值。 - AJF
1
@Shepmaster 感谢您指出我的错误。您链接的问题与我的非常相似,但我想知道为什么它可以使用 if 比较而不能使用 match - robfuscator
5
这是因为编译器对它们的处理方式不同。if语句委托给了PartialEq的实现。match没有这种特殊处理,因此需要一些帮助。这两个结构体本来就被编译成不同的形式,所以我不确定这对于编译器内部会如何工作。if条件成为简单的分支,而match似乎在编译时会变成类似于switch的结构(根据LLVM IR)。 - Simon Whitehead
如果只有两种可能的结果(就像您的第二个示例中一样),if else 是一个完全可以使用的良好结构。 - ljedrz
2个回答

75
我想知道为什么使用if时比较起作用,但不使用match

问题并不在于if,而是因为您在条件中使用了==if语句中的条件可以是任何类型为bool的表达式;您只是选择在那里使用====运算符实际上是与PartialEq特性相关联的函数。此特性可以针对任何一对类型进行实现。而且,为了方便起见,String具有对PartialEq<str>PartialEq<&str>等的实现 - 反之亦然。
另一方面,match表达式使用模式匹配进行比较,而不是==。像"holla!"这样的&'static str字面量是有效的模式,但它永远无法匹配String,因为它是完全不同的类型。
模式匹配让你可以简洁地比较复杂结构的部分,即使整个结构不相等,也可以将变量绑定到匹配的部分。虽然对于字符串来说没有太大好处,但它对于其他类型非常强大,并且具有与“==”完全不同的目的。
请注意,您可以使用模式匹配和“if”通过使用“if let”结构来代替。您的示例将如下所示:
if let "holla!" = &*s {
    println!("it worked!");
}

相反,使用match内部的==的一种方法如下:

match s {
    _ if s == "holla!" => println!("it worked!"),
    _ => println!("nothing"),
}

或者,如@ljedrz建议:

match s == "holla!" {
    true => println!("it worked!"), 
    _ => println!("nothing")
}

18

正如 @peter-hall 所说,由于 match 表达式使用模式匹配,而不是与 PartialEq 特质相关的 ==,所以存在类型不匹配的问题。

还有第二种方法可以解决这个问题,即将你的 String 强制转换为一个 &str(字符串切片):

match &s[..] {
    "holla!" => println!("it worked!"),
    "Hallo!" => println!("with easy to read matches !"),
    _ => println!("nothing"),
}

2
顶级答案!由于match和String都是特殊的,我们希望编译器能够为我们完成这个任务。 - Sam Liddicott
1
@SamLiddicott 问题在于核心语言没有定义字符串,所以为其制定特例并不理想。因此,需要一种通用的方法来解决这个问题。可以通过使用 trait 来实现,每当出现匹配时使用该 trait,但我不确定这样做的影响。 - Naeio
更新这个答案使用.as_str()会不会更好,这样可以保留所有权呢? - undefined

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