在涉及线和地图的情况下对所有权感到困惑

Rob*_*ond 3 ownership rust

fn problem() -> Vec<&'static str> {
    let my_string = String::from("First Line\nSecond Line");
    my_string.lines().collect()
}
Run Code Online (Sandbox Code Playgroud)

这失败并出现编译错误:

  |
7 |     my_string.lines().collect()
  |     ---------^^^^^^^^^^^^^^^^^^
  |     |
  |     returns a value referencing data owned by the current function
  |     `my_string` is borrowed here
Run Code Online (Sandbox Code Playgroud)

我明白这个错误意味着什么 - 它是为了阻止你返回对超出范围的值的引用。查看所涉及函数的类型签名后,问题似乎出在lines方法上,该方法借用了调用它的字符串。但这有什么关系呢?我正在遍历字符串的行以获取部件的向量,我返回的是这个“新”向量,而不是任何会(非法)直接引用my_string.

(我知道我可以很容易地通过使用字符串文字而不是转换为“拥有”的字符串来修复这个特定的例子String::from。这是一个重现问题的玩具示例——在我的“真实”代码中,字符串变量是从文件中读取,所以我显然不能使用文字。)

对我来说更神秘的是,对我来说应该遇到同样问题的函数的以下变化可以正常工作:

fn this_is_ok() -> Vec<i32> {
    let my_string = String::from("1\n2\n3\n4");
    my_string.lines().map(|n| n.parse().unwrap()).collect()
}
Run Code Online (Sandbox Code Playgroud)

原因不能map做一些魔术,因为这也失败了:

fn also_fails() -> Vec<&'static str> {
    let my_string = String::from("First Line\nSecond Line");
    my_string.lines().map(|s| s).collect()
}
Run Code Online (Sandbox Code Playgroud)

我已经玩了很长一段时间了,在里面尝试了各种不同的功能map- 有些通过,有些失败,老实说,我不知道有什么区别。所有这一切让我意识到,我对 Rust 的所有权/借用规则在非平凡情况下的工作方式知之甚少,尽管我认为我至少了解了基础知识。因此,如果有人能给我一个相对清晰和全面的指南,说明所有这些示例中发生的事情,以及如何以某种直接的方式修复那些失败的示例,我将不胜感激!

Apl*_*123 6

关键在于lines:产生的值的类型&str。为了避免不必要的克隆,lines实际上返回对其调用的字符串切片的引用,并且当您将其收集到 a 时Vec,该Vec元素只是对字符串切片的引用。所以,当然,当你的函数退出并且字符串被删除时,里面的引用Vec将被删除并且无效。请记住,&str是借用字符串,并且String是自有字符串。

解析工作是因为您将这些&strs 读入 一个i32,因此数据被传输到一个新值并且您不再需要对原始字符串的引用。

要解决您的问题,只需使用str::to_owned将每个元素转换为String

fn problem() -> Vec<String> {
    let my_string = String::from("First Line\nSecond Line");
    my_string.lines().map(|v| v.to_owned()).collect()
}
Run Code Online (Sandbox Code Playgroud)

应该注意的是,它to_string也有效,这to_owned实际上是ToOwnedtrait 的一部分,因此它对其他借用类型也很有用。

对于对大小值的引用(str未调整大小,因此这不适用),例如Iterator<Item = &i32>,您可以简单地使用Iterator::cloned克隆每个元素,使它们不再是引用。

另一种解决方案是将 theString作为参数,因此它以及对它的引用可以超过函数的作用域:

fn problem(my_string: &str) -> Vec<&str> {
    my_string.lines().collect()
}
Run Code Online (Sandbox Code Playgroud)