rust - 为什么“临时是块末尾表达式的一部分”是一个错误?
问题描述
这可能是我不了解借阅检查器的一些技术性的教科书案例,但如果有人能为我解决这个问题会很好。
我有这段(非常简化的)代码,编译得非常好。
pub struct Example(pub Vec<String>);
impl Example {
pub fn iter(&self) -> impl Iterator<Item=&String> {
self.0.iter()
}
}
pub fn some_condition(_: &str) -> bool {
// This is not important.
return false;
}
pub fn foo() -> bool {
let example = Example(vec!("foo".to_owned(), "bar".to_owned()));
let mut tmp = example.iter();
tmp.all(|x| some_condition(x))
}
pub fn main() {
println!("{}", foo());
}
但是,我尝试的第一件事(在我看来,应该与上述相同)是tmp
完全省略临时变量,如下所示
pub fn foo() -> bool {
let example = Example(vec!("foo".to_owned(), "bar".to_owned()));
example.iter().all(|x| some_condition(x))
}
但是这个版本会产生以下错误。
error[E0597]: `example` does not live long enough
--> so_temporary.rs:23:3
|
23 | example.iter().all(|x| some_condition(x))
| ^^^^^^^-------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
24 | }
| -
| |
| `example` dropped here while still borrowed
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `impl std::iter::Iterator`
|
= note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.
现在,显然,错误末尾的注释是一个很好的建议,这就是我引入临时解决问题的原因。但我不明白为什么这可以解决问题。我的tmp
变量的生命周期与example.iter()
直接嵌入表达式的生命周期有什么不同,这使得一个工作和一个失败?
解决方案
这与为什么我在返回值中得到“活得不够长”的答案基本相同?并且它在错误本身中有所解释,但我会详细说明。此行为与普通块表达式相同:
pub struct Example(pub Vec<String>);
impl Example {
pub fn iter(&self) -> impl Iterator<Item=&String> {
self.0.iter()
}
}
pub fn main() {
let foo = {
let example = Example(vec!("foo".to_owned(), "".to_owned()));
example.iter().all(String::is_empty)
};
println!("{}", foo);
}
error[E0597]: `example` does not live long enough
--> src/main.rs:12:9
|
12 | example.iter().all(String::is_empty)
| ^^^^^^^-------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
13 | };
| -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `impl Iterator`
| |
| `example` dropped here while still borrowed
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
12 | let x = example.iter().all(String::is_empty); x
| ^^^^^^^ ^^^
临时值的范围通常是创建它们的语句。在上面的代码example
中是一个变量,它在块的末尾被销毁。但是,example.iter()
创建一个临时的impl Iterator
,其临时范围是完整的let foo = ...
语句。因此,评估时的步骤是:
- 评估结果
example.iter().all(...)
- 降低
example
- 将结果分配给
foo
- 降低
impl Iterator
您可能会看到这可能出错的地方。引入变量的原因是因为它迫使任何临时对象更快地被删除。谈函数时,情况略有不同,但效果是一样的:
在函数体的最终表达式中创建的临时变量在函数体中绑定的任何命名变量之后被删除,因为没有更小的封闭临时范围。
关于评论:
当
impl Iterator
被替换为std::slice::Iter<'_, i32>
(在 pretzelhammer 的示例中)时它起作用的原因是因为 drop checker知道它slice::Iter
不会example
在 drop 时访问,而它必须假设这样impl Iterator
做。它使用的原因
fn my_all(mut self, ...)
(在 Peter Hall 的示例中)是因为all
通过引用获取迭代器,但my_all
通过值获取它。临时impl Iterator
变量在表达式结束之前被消耗和销毁。
通过查看与此相关的各种 Rust 问题,很明显有些人会认为这是一个错误。这绝对不明显,{ ...; EXPR }
可能{ ...; let x = EXPR; x }
会有所不同。但是,由于添加了诊断和文档来加强和解释这种行为,我不得不假设这些临时范围规则允许更合理的代码。
推荐阅读
- sql-server - SQL Server:使用带有反向团队名称的分区依据
- python-3.x - 美丽的汤 - 制作清单
- python - 如何从奇怪的 JSON 响应中获取价值
- javascript - 单击按钮将页面向下滚动到另一个 React 组件
- html - 如何将图标与标题而不是段落对齐?
- javascript - 在Javascript中将递归函数的结果连接到数组中的最快方法是什么?
- pytorch - 创建一个张量视图(无数据副本),其中删除了一行/列
- python - 如何根据用户输入打印输出
- python - 我收到 InvalidArgumentError 因为索引显然超出范围。为什么?
- angular - 有人可以分享有关修复此错误的任何见解吗?