为什么在使用Vec::contains时,&str不能强制转换为&String?

12

一个朋友问我解释Rust中的以下怪异行为。我无法解释,因此提出这个问题:

fn main() {
    let l: Vec<String> = Vec::new();
    //let ret = l.contains(&String::from(func())); // works
    let ret = l.contains(func());  // does not work
    println!("ret: {}", ret);
}

fn func() -> & 'static str {
    "hello"
}

在 Rust Playground 上查看示例

编译器会报出以下错误信息:

error[E0308]: mismatched types
 --> src/main.rs:4:26
  |
4 |     let ret = l.contains(func());  // does not work
  |                          ^^^^^^ expected struct `std::string::String`, found str
  |
  = note: expected type `&std::string::String`
             found type `&'static str`

换句话说,&str不能与&String强制转换。

起初我认为这与'static有关,但那是一个误导。

注释掉的行会以额外的分配成本修复示例。

我的问题:

  • 为什么&str不能与&String强制转换?
  • 有没有办法使调用contains不需要额外的分配?

似乎 String 可以强制类型转换为 str,但是反过来不行。 - Laserallan
3
Vec::contains is too restrictive也与此相关。简单来说,修复这个问题让 contains 方法可以接受可比较的引用是可能的,但是这样做会破坏其他代码。这是 Rust 中一个非常容易出错的地方。 - Yann Vernier
2
可能是什么是Rust的Stringstr之间的区别?的重复问题。 - Nikolay Lebedev
我认为这比那更微妙。我们知道Stringstr之间的区别。这是一个强制转换的问题。 - Edd Barrett
@EddBarrett,我认为我的答案清楚地阐明了为什么str强制转换为String没有意义,以及您如何更改代码以避免分配。 - Marko Popovic
显示剩余3条评论
3个回答

11

你的第一个问题已经被@Marko回答了。

你的第二个问题也很容易回答,只需使用闭包:

let ret = l.iter().any(|x| x == func());

编辑:

不再是“真正”的答案,但我在这里放着给可能对此有兴趣的人一个解决方案。


你确实已经解决了这个问题,但那不是我想要的。你能否仍然使用“contains”完成吗? - Edd Barrett
很抱歉,但在你看来有什么区别吗?它们都是一样的 :) - hellow
你的观点在实际情况下没错,但这并不能帮助我理解在此处起作用的类型系统限制。 - Edd Barrett
好的,但你的问题是:“有没有办法使代码在不进行额外分配的情况下运行?”,我回答了这个问题,结果还被踩了? 好吧 :/ 继续。 - hellow
顺便说一句,不是我给你的投票打了反对票。我会修改我的问题,让它更加清晰明了。 - Edd Barrett
好的,抱歉误解了。 :) - hellow

7

3

std::string::String是一种可扩展的、堆分配的数据结构,而字符串切片(str)是一个不可变的固定长度字符串在内存中的某个位置。字符串切片作为借用类型使用,通过&str进行访问。可以将其视为查看某些字符串数据,这些数据驻留在内存中的视图。这就是为什么str不会强制转换为String,而另一种方式完全有意义。您在内存中有一个堆分配的String,并且您想要使用一个视图(即字符串切片)来查看该字符串。

回答您的第二个问题。以当前形式没有办法使代码运行。您需要将其更改为字符串切片的向量(这样就不会有额外的分配),或者使用除了contains方法之外的其他东西。


我认为这并不完全回答了我的问题。&str&String都是借用类型。 - Edd Barrett
@EddBarrett 你说得对。我希望现在编辑过的答案能够回答你的问题。 - Marko Popovic

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