【问题标题】:Why does the compiler tell me to consider using a `let` binding" when I already am?为什么编译器告诉我在我已经使用 `let` 绑定时要考虑使用?
【发布时间】:2015-03-06 06:03:10
【问题描述】:

我的错误是什么以及如何解决?

fn get_m() -> Vec<i8> {
    vec![1, 2, 3]
}

fn main() {
    let mut vals = get_m().iter().peekable();
    println!("Saw a {:?}", vals.peek());
}

(playground)

编译器的错误提示“考虑使用let 绑定”——但我已经是:

error[E0597]: borrowed value does not live long enough
 --> src/main.rs:6:45
  |
6 |     let mut vals = get_m().iter().peekable();
  |                    -------                  ^ temporary value dropped here while still borrowed
  |                    |
  |                    temporary value created here
7 |     println!("Saw a {:?}", vals.peek());
8 | }
  | - temporary value needs to live until here
  |
  = note: consider using a `let` binding to increase its lifetime

这显然是一个新手问题——尽管我认为此时我已经写了足够多的 Rust,以至于我已经掌握了借用检查器......显然我没有。

这个问题类似于Using a `let` binding to increase value lifetime,但不涉及将表达式分解为多个语句,所以我认为问题并不相同。

【问题讨论】:

  • 关于阅读错误消息的提示:“借用值的寿命不够长”。哪个借来的价值?看看跨度,它是get_m()。 “考虑使用 let 绑定来增加它的生命周期”。也就是说,get_m() 部分上使用 let 绑定。​​
  • 感谢@ChrisMorgan!曲线的范围是一个很好的线索。

标签: rust borrow-checker


【解决方案1】:

问题在于 Peekable 迭代器一直存在于函数的末尾,但它包含对 get_m 返回的向量的引用,该向量仅持续与包含该调用的语句一样长。

这里其实有很多事情要做,让我们一步一步来:

  • get_m 分配并返回一个向量,类型为 Vec&lt;i8&gt;
  • 我们拨打电话.iter()。令人惊讶的是,Vec&lt;i8&gt; 没有 iter 方法,也没有实现任何具有该方法的特征。所以这里有三个子步骤:
    • 任何方法调用都会检查其self 值是否实现Deref 特征,并在必要时应用它。 Vec&lt;i8&gt; 确实实现了Deref,所以我们隐式调用它的deref 方法。但是,deref 通过引用获取其self 参数,这意味着get_m() 现在是出现在左值上下文中的右值。在这种情况下,Rust 创建了一个临时值来保存该值,并传递一个对它的引用。 (留意这个临时的!)
    • 我们调用deref,产生一个&amp;[i8] 类型的切片,借用向量的元素。
    • 这个切片实现了SliceExt trait,确实有一个iter 方法。最后!这个iter 还通过引用获取它的self 参数,并返回一个std::slice::Iter 持有对切片的引用。
  • 我们拨打电话.peekable()。和以前一样,std::slice::Iter 没有peekable 方法,但它确实实现了IteratorIteratorExt 为每个Iterator 实现; IteratorExt 确实有一个peekable 方法。这会按值获取其self,因此Iter 被消耗,我们得到一个std::iter::Peekable 作为回报,再次持有对切片的引用。
  • 这个Peekable 然后绑定到变量vals,它一直存在到函数的末尾。
  • 临时持有原始Vec&lt;i8&gt;Peekable 所指的元素)现在死亡。哎呀。这是借来的价值不够长。

但临时人员死在那里只是因为这是临时人员的规则。如果我们给它一个名字,那么只要它的名字在范围内,它就会一直存在:

let vec = get_m();
let mut peekable = vec.iter().peekable();
println!("Saw a {:?}", vals.peek());

我认为这就是故事。然而,仍然让我感到困惑的是,为什么即使没有名字,这个临时的也不会活得更长。 Rust 引用说:“临时的生命周期等于指向它的任何引用的最大生命周期。”但这显然不是这里的情况。

【讨论】:

  • 非常感谢您花时间在这里挖掘!快速提问:我在哪里可以学习诸如“任何方法调用都会检查其自身值是否实现 Deref 特征,并在必要时应用它”之类的东西?我的意思是,语言规范是一个来源;但是有什么更高层次的吗?
  • 嗯,我是从阅读 Rust 参考中得到的:doc.rust-lang.org/reference.html 请特别参阅“字段表达式”部分。但这并不是很清楚。有人告诉我,该参考资料将很快进行重大更新。另一个来源是 Deref 特征本身的文档:doc.rust-lang.org/std/ops/trait.Deref.html 但这也不是很好。我做了很多实验。
  • 我不确定临时生命周期规则的最佳参考是什么,但参考中的引用肯定是不正确的。这篇博客文章涵盖了当前规则中的许多推理,但我认为它与已实施的规则并不完全同步:smallcultfollowing.com/babysteps/blog/2014/01/09/… 希望我们会尽快更新参考(另请注意是一个被接受但未实现的 RFC,在某些情况下会调整规则,#66)
【解决方案2】:

发生这种情况是因为您试图在 get_m() 内部的实际向量上运行 .iter().peekable(),而 vals 正在重新引用该向量。

基本上,你想要这样的东西:

fn get_m() -> Vec<i8> {
    vec![1, 2, 3]
}

fn main() {
    let vals = get_m();
    let mut val = vals.iter().peekable();
    println!("Saw a {:?}", val.peek());
}

(Playground)

结果:

Saw a Some(1)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 2013-10-10
    • 2019-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多