rust - 为什么 "the temporary is part of an expression at the end of a block"是一个错误?

标签 rust borrow-checker temporary

这可能是我不了解借阅检查器的一些技术性的教科书案例,但如果有人能为我解决这个问题会很好。
我有这段(非常简化的)代码,编译得非常好。

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()直接嵌入到表达式中,使一项工作,一项失败?

最佳答案

这与 Why do I get "does not live long enough" in a return value? 的答案基本相同并且它在错误本身中有所解释,但我会详细说明。此行为与普通 block 表达式相同:

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
   |         ^^^^^^^                                     ^^^
scope of temporary values通常是创建它们的语句。在上面的代码中 example是一个变量,它在 block 的末尾被销毁。但是,example.iter()创建一个临时 impl Iterator它的临时范围是完整的let foo = ...陈述。因此,评估时的步骤是:
  • 评估 example.iter().all(...) 的结果
  • 掉落example
  • 将结果分配给 foo
  • 掉落impl Iterator

  • 您可能会看到这可能出错的地方。引入变量的原因是因为它迫使任何临时对象更快地被删除。谈函数时,情况略有不同,但效果是一样的:

    Temporaries that are created in the final expression of a function body are dropped after any named variables bound in the function body, as there is no smaller enclosing temporary scope.


    关于评论:
  • impl Iterator 时起作用的原因替换为 std::slice::Iter<'_, i32> (在 pretzelhammer 的例子中)是因为 drop checker 知道 slice::Iter不访问 example下降,而它必须假设 impl Iterator做。
  • 它适用于 fn my_all(mut self, ...) 的原因(在 Peter Hall 的例子中)是因为 all通过引用获取迭代器,但 my_all按值(value)计算。临时impl Iterator在表达式结束之前被消耗和销毁。

  • 通过查看与此相关的各种 Rust 问题,很明显有些人会认为这是一个错误。 { ...; EXPR } 绝对不明显和 { ...; let x = EXPR; x }可能会有所不同。但是,由于添加了诊断和文档来加强和解释这种行为,我不得不假设这些临时范围规则允许更合理的代码。

    关于rust - 为什么 "the temporary is part of an expression at the end of a block"是一个错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65972165/

    相关文章:

    rust - "borrowed value does not live long enough"trie插入错误

    临时对象的 C++ 调用函数

    rust - 你能控制借用结构还是借用字段?

    c - 我可以在 C 程序中同时打开多少个临时文件?

    c++ - 是否可以更改临时对象并将其作为参数传递?

    rust - 闭包参数的生命周期注解

    Rust:如何使用 async-std + TLS + HTTP 代理(http 隧道)?

    rust - 哪些 Rust 1.2 容器支持特征对象?

    function - 无法创建本地函数,因为 "can' t 在 fn 项中捕获动态环境”

    rust - 为什么不能在同一结构中存储值和对该值的引用?