在 for 循环中处理 "borrowed value does not live long enough"
Dealing with "borrowed value does not live long enough" within for loops
我正在抓取的网站要求我查询 HTML 页面的标题标签以及一些其他元素,以查看我是否可以辨别文章的标题。
我创建了一个 HashMap<&str, u8>
并立即 .insert(title_tag_text, 1)
,查询 header 元素,然后我希望将 header 标签的文本插入哈希映射同样,但我收到错误 borrowed value does not live long enough
.
我不确定我是否理解,因为我认为我正确地将 std::string::String
取消引用为应该实现 Copy
特征的 &str
?不幸的是,我怀疑我计划实施的下一个代码有类似的问题。
let mut title_candidates: HashMap<&str, u8> = HashMap::new();
let title_tag_text: String = Selector::parse("title")
.ok()
.and_then(|selector| html_document.select(&selector).next())
.map(|elem| elem.inner_html())?;
title_candidates.insert(&*title_tag_text, 1);
Selector::parse("h1, h2, h3, .title")
.ok()
.as_ref()
.map(|selector| html_document.select(selector))?
.map(|elem| elem.inner_html()) // std::string::String
.for_each(|title| {
*title_candidates.entry(&*title).or_insert(0) += 1;
// if title_tag_text.contains(&*title.as_str()) {
// *title_candidates.entry(&*title_tag_text) += 1;
// }
});
error[E0597]: `title` does not live long enough
--> src/main.rs:140:39
|
125 | let mut title_candidates: HashMap<&str, u8> = HashMap::new();
| -------------------- lifetime `'1` appears in the type of `title_candidates`
...
140 | *title_candidates.entry(&*title).or_insert(0) += 1;
| -------------------------^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `title` is borrowed for `'1`
...
144 | });
| - `title` dropped here while still borrowed
您的 HashMap
的键是 &str
类型。这意味着 HashMap
仅持有对 str
的 引用 而不是 str
本身。因此,要使 HashMap
中的数据有效,对 str
的引用至少应与 HashMap
一样长。现在的问题是,String
是在 .map(|elem| elem.inner_html())
中创建的,因此在该语句完成后它会被删除。
相反,创建一个 HashMap
,它使用 owned String
s 而不是引用。以下是您可以根据自己的情况进行调整的简化示例:
fn main() {
let mut data: HashMap<String, i32> = HashMap::new();
(0..20)
.map(|i| (i % 10).to_string())
.for_each(|text| {
*data.entry(text).or_insert(0) += 1;
});
}
在这里,.map(|i| (i % 10).to_string())
创建了一个 String
,然后将其所有权传递给 data.entry(text)
中的 HashMap
,从而避免了引用生命周期中的任何不匹配。
您案例的问题很常见,我曾多次偶然发现。
Rust 没有垃圾收集器,这是众所周知的,但我们通常很难理解它的实际含义。
在您的例子中,您试图在地图中存储对字符串的引用,该字符串仅存在 for_each 函数。
问题来了,那个函数returns时会发生什么?
当该函数 returns 在该函数中创建的对象将是 free
d 并且如果您 &str
指向的字符串被释放,您的 &str
将指向一个位置不再属于你
如果您想使用引用,您需要确保只要引用还在使用,它们引用的内容就会存在。
在你上面的例子中,简单地使用一个拥有的字符串将解决这个问题,在这种情况下,这个问题将由 hashmap 拥有,并且只要 hashmap 存在,就会存在。
所以你应该将你的 hashmap 签名编辑为 HashMap<String,u8>
并在插入时使用 .to_string()
或 .to_owned()
简单地传递 &str
的拥有副本
我正在抓取的网站要求我查询 HTML 页面的标题标签以及一些其他元素,以查看我是否可以辨别文章的标题。
我创建了一个 HashMap<&str, u8>
并立即 .insert(title_tag_text, 1)
,查询 header 元素,然后我希望将 header 标签的文本插入哈希映射同样,但我收到错误 borrowed value does not live long enough
.
我不确定我是否理解,因为我认为我正确地将 std::string::String
取消引用为应该实现 Copy
特征的 &str
?不幸的是,我怀疑我计划实施的下一个代码有类似的问题。
let mut title_candidates: HashMap<&str, u8> = HashMap::new();
let title_tag_text: String = Selector::parse("title")
.ok()
.and_then(|selector| html_document.select(&selector).next())
.map(|elem| elem.inner_html())?;
title_candidates.insert(&*title_tag_text, 1);
Selector::parse("h1, h2, h3, .title")
.ok()
.as_ref()
.map(|selector| html_document.select(selector))?
.map(|elem| elem.inner_html()) // std::string::String
.for_each(|title| {
*title_candidates.entry(&*title).or_insert(0) += 1;
// if title_tag_text.contains(&*title.as_str()) {
// *title_candidates.entry(&*title_tag_text) += 1;
// }
});
error[E0597]: `title` does not live long enough
--> src/main.rs:140:39
|
125 | let mut title_candidates: HashMap<&str, u8> = HashMap::new();
| -------------------- lifetime `'1` appears in the type of `title_candidates`
...
140 | *title_candidates.entry(&*title).or_insert(0) += 1;
| -------------------------^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `title` is borrowed for `'1`
...
144 | });
| - `title` dropped here while still borrowed
您的 HashMap
的键是 &str
类型。这意味着 HashMap
仅持有对 str
的 引用 而不是 str
本身。因此,要使 HashMap
中的数据有效,对 str
的引用至少应与 HashMap
一样长。现在的问题是,String
是在 .map(|elem| elem.inner_html())
中创建的,因此在该语句完成后它会被删除。
相反,创建一个 HashMap
,它使用 owned String
s 而不是引用。以下是您可以根据自己的情况进行调整的简化示例:
fn main() {
let mut data: HashMap<String, i32> = HashMap::new();
(0..20)
.map(|i| (i % 10).to_string())
.for_each(|text| {
*data.entry(text).or_insert(0) += 1;
});
}
在这里,.map(|i| (i % 10).to_string())
创建了一个 String
,然后将其所有权传递给 data.entry(text)
中的 HashMap
,从而避免了引用生命周期中的任何不匹配。
您案例的问题很常见,我曾多次偶然发现。 Rust 没有垃圾收集器,这是众所周知的,但我们通常很难理解它的实际含义。
在您的例子中,您试图在地图中存储对字符串的引用,该字符串仅存在 for_each 函数。
问题来了,那个函数returns时会发生什么?
当该函数 returns 在该函数中创建的对象将是 free
d 并且如果您 &str
指向的字符串被释放,您的 &str
将指向一个位置不再属于你
如果您想使用引用,您需要确保只要引用还在使用,它们引用的内容就会存在。
在你上面的例子中,简单地使用一个拥有的字符串将解决这个问题,在这种情况下,这个问题将由 hashmap 拥有,并且只要 hashmap 存在,就会存在。
所以你应该将你的 hashmap 签名编辑为 HashMap<String,u8>
并在插入时使用 .to_string()
或 .to_owned()
&str
的拥有副本