【问题标题】:Rust temporary variable lifetime in method chaining方法链中的 Rust 临时变量生命周期
【发布时间】:2019-03-22 02:45:21
【问题描述】:

我试图通过将 Rust 与我更熟悉的 C++ 中的类似概念进行比较来学习 Rust 的生命周期规则。大多数时候,我的直觉非常有效,我可以理解规则。但是,在以下情况下,我不确定我的理解是否正确。

在 Rust 中,临时值的生命周期是其语句的结束,除非最后一个临时值使用 let 绑定到名称。

struct A(u8);
struct B(u8);

impl A {
    fn get_b(&mut self) -> Option<B> {
        Some(B(self.0))
    }
}

fn a(v: u8) -> A {
    A(v)
}

// temporary A's lifetime is the end of the statement
// temporary B binds to a name so lives until the enclosing block
let b = a(1).get_b();

// temporary A's lifetime is the end of the statement
// temporary B's lifetime extends to the enclosing block,
// so that taking reference of temporary works similar as above
let b = &a(2).get_b();

如果临时值在if 条件下,根据reference,生命周期改为限制为条件表达式。

// Both temporary A and temporary B drops before printing some
if a(3).get_b().unwrap().val <= 3 {
    println!("some");
}

现在问题来了:

如果将let 放入if 条件中,由于模式匹配,我们将绑定到临时值的内部。我希望由let 绑定的临时值扩展到封闭块,而其他临时值的生命周期仍应受 if 条件限制。

(在这种情况下,实际上所有内容都被复制了,我想说即使是临时的 B 也可以删除,但这是一个单独的问题。)

但是,两个临时对象的生命周期都延长到封闭的 if 块。

// Both temporary A and temporary B's lifetime are extended to the end of the enclosing block,
// which is the if statement
if let Some(B(v @ 0...4)) = a(4).get_b() {
    println!("some {}", v);
}

这应该被视为 Rust 中的不一致吗?还是我误解了,有一个一致的规则可以解释这种行为?

完整代码示例:

注意 Rust 的输出是

some 4
Drop B 4
Drop A 4

而 C++ 的输出是

Drop A 4                                                                                                                                                                         
some 4                                                                                                                                                                           
Drop B 4

我已经阅读了这个 Reddit thread 和 Rust issue,我认为它们非常相关,但我仍然找不到适用于 Rust 中所有情况的明确的生命周期规则。

更新:

我不清楚的是为什么关于if 条件表达式的临时生命周期规则适用于if let。我认为let Some(B(v @ 0...4)) = a(4).get_b() 应该是条件表达式,因此临时A 的生命周期应该受此限制,而不是整个if 语句。

将临时 B 的生命周期延长到整个 if 语句的行为是预期的,因为这是模式匹配借用的。

【问题讨论】:

  • 你借用&amp;mut self,所以我期待这个结果,尝试通过消费self => get_b(self)
  • @Stargateur get_b 确实借用了&amp;mut self,但返回值不包含生命周期,因此它不需要“继续借用”。我认为这里发生的事情是编译器将整个 if let 表达式视为“当前语句”(即,它与 let tmp = a(4).get_b(); if let Some(B(_)) = tmp { ... } 的工作方式不同,后者与类似的 C++ 代码具有相同的输出。)跨度>
  • @trentcl 这个问题解释了为什么,github.com/rust-lang/rust/issues/37612#issuecomment-304626464,模式匹配将借用扩展到模式匹配的范围。 let b = a(4).get_b(); if let Some(B(v@ 0...4)) = b { println!("some {}", v); } 应该按预期工作。
  • 不是borrow被延长,而是临时的生命周期被延长,即使它没有被借用。至少这是我的解释
  • @Stargateur 如果get_b 消耗self,那么情况就不同了。临时 A 将被放入 get_b 体内,因为它已移到那里。这与在语句末尾删除的效果不同。

标签: rust


【解决方案1】:

if let 构造只是 match 构造的语法糖。 let Some(B(v @ 0...4)) = a(4).get_b() 不是在常规 if 表达式中使用的条件,因为它不是计算结果为 bool 的表达式。鉴于你的例子:

if let Some(B(v @ 0...4)) = a(4).get_b() {
    println!("some {}", v);
}

它的行为与以下示例完全相同。没有例外。 if let 在类型或借用检查器运行之前被重写为 match

match a(4).get_b() {
    Some(B(v @ 0...4)) => {
        println!("some {}", v);
    }
    _ => {}
}

临时角色的寿命与他们在匹配块中一样长,因为它们有时会派上用场。就像你的最后一个函数是fn get_b(&amp;mut self) -&gt; Option&lt;&amp;B&gt;,如果临时函数没有在整个匹配块中存活,那么它就不会通过borrowck。

if 条件不遵循相同的规则,因为 if 条件中的最后一个函数调用不可能持有对任何东西的引用。他们必须评估为一个普通的bool

见:

【讨论】:

    猜你喜欢
    • 2012-05-19
    • 2012-05-19
    • 1970-01-01
    • 2020-12-31
    • 2013-07-03
    • 2021-03-23
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多