方法链中的 Rust 临时变量生命周期

标签 rust

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

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

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 条件中,由于模式匹配,我们将绑定(bind)到临时值的内部。我希望 let 绑定(bind)的临时值扩展到封闭 block ,而其他临时值的生命周期仍应受 if 条件限制。

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

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

// 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 中的不一致吗?还是我误解了,有一个一致的规则可以解释这种行为?

完整代码示例:

  • playground
  • C++ 中实现了同样的事情符合我的期望

注意 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 语句的行为是预期的,因为模式匹配借用了它。

最佳答案

if let construct 只是 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);
}

它的行为与下面的例子完全一样。没有异常(exception)。 if let被重写为 match在类型检查器或借用检查器运行之前。

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

临时对象只要在匹配 block 中就可以存活,因为它们有时会派上用场。就像你的最后一个函数是 fn get_b(&mut self) -> Option<&B> , 如果临时没有在整个匹配 block 中存活,那么它就不会通过 borrowck。

If 条件语句不遵循相同的规则,因为 if 条件语句中的最后一个函数调用不可能包含对任何内容的引用。他们必须评估为普通 bool .

参见:

关于方法链中的 Rust 临时变量生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55292164/

相关文章:

rust - gstreamer rust 为 x264enc 上设置的比特率获取人类可读的输出

algorithm - QOI 索引哈希函数的按位加权和

rust - 什么特征界限可用于匹配 Rust 中的所有无符号整数类型?

rust - transmute::< &'a Arc<T>, &' a Weak<T>>(...) 是否安全?

rust - 如何设置返回值的生命周期?

rust - 我是否必须担心 `Rc` 的开销?

methods - 如何在 HashMap 中存储方法指针并调用它们

rust - 为什么我不能将 `&Iterator<Item = &String>` 用作迭代器?

syntax - 奇怪的语法错误 'error: expected one of ` ,` or ` :`, found ` else`'

rust - Cargo.toml 可以包含自定义属性吗