Sil*_*olo 8 temporary rust borrow-checker
这可能是我不理解借用检查器的一些技术细节的教科书案例,但如果有人能帮我解决这个问题会很好。
我有这个(令人难以置信的简化)代码块,它编译得非常好。
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());
}
Run Code Online (Sandbox Code Playgroud)
但是,我尝试的第一件事(在我看来,应该等同于上述内容)是tmp完全删除临时变量,如下所示
pub fn foo() -> bool {
let example = Example(vec!("foo".to_owned(), "bar".to_owned()));
example.iter().all(|x| some_condition(x))
}
Run Code Online (Sandbox Code Playgroud)
但是这个版本会产生以下错误。
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());
}
Run Code Online (Sandbox Code Playgroud)
现在,显然,错误末尾的注释是一个很好的建议,这就是我引入临时解决问题的原因。但我不明白为什么这可以解决问题。我的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);
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
临时值的范围通常是创建它们的语句。上面的代码中example有一个变量,它在块的末尾被销毁。但是,example.iter()创建一个临时的impl Iterator,其临时范围是完整的let foo = ...语句。因此评估时的步骤是:
example.iter().all(...)examplefooimpl Iterator您可能会看到哪里出了问题。引入变量有效的原因是它迫使任何临时变量更快地被删除。谈论功能时情况略有不同,但效果是一样的:
在函数体的最终表达式中创建的临时变量将被删除在函数体中绑定的任何命名变量之后,因为没有更小的封闭临时作用域。
关于评论:
impl Iterator它在替换为(在 pretzelhammer 的示例中)时起作用的原因std::slice::Iter<'_, i32>是因为放置检查器知道在放置时不会slice::Iter访问example,而它必须假设impl Iterator可以访问。
它起作用的原因fn my_all(mut self, ...)(在 Peter Hall 的示例中)是因为all通过引用获取迭代器,但my_all通过值获取它。临时变量impl Iterator在表达式结束之前被消耗和销毁。
从与此相关的各种 Rust 问题来看,很明显有些人会认为这是一个错误。这绝对不明显,{ ...; EXPR }并且{ ...; let x = EXPR; x }可能有所不同。然而,由于添加了诊断和文档来强化和解释这种行为,我必须假设这些临时范围规则允许更合理的代码。
| 归档时间: |
|
| 查看次数: |
137 次 |
| 最近记录: |