我正在尝试通过将Rust的生命周期规则与我更熟悉的C++中的类似概念进行比较来学习它。大多数情况下,我的直觉非常好,我可以理解这个规则。然而,在以下情况下,我不确定我的理解是否正确。
如果临时值在一个
我阅读了这篇 Reddit thread 和 Rust issue,认为它们非常相关,但我仍然找不到适用于所有 Rust 情况的明确的生命周期规则。
将临时B的生命周期延长到整个
在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
条件语句中,根据参考文献,其生命周期将被限制为条件表达式。// Both temporary A and temporary B drops before printing some
if a(3).get_b().unwrap().val <= 3 {
println!("some");
}
现在来谈谈问题:
如果在if
条件中使用let
,由于模式匹配,我们绑定到临时值的内部部分。我期望由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中的一种不一致性?或者我有误解,存在一个一致的规则可以解释这种行为?
完整的代码示例:
- 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
语句的行为是可以预期的,因为它被模式匹配所借用。
&mut self
,因此我期望这个结果,尝试通过消费self
=>get_b(self)
。 - Stargateurget_b
确实借用了&mut self
,但返回值不包含生命周期,因此它不需要“保持借用”。我认为这里发生的情况是编译器将整个if let
表达式视为“当前语句”(即,它与类似的 C++ 代码的输出不同:let tmp = a(4).get_b(); if let Some(B(_)) = tmp { ... }
)。 - trentget_b
消耗了self
,那就是另一种情况。临时变量 A 将在get_b
函数体内被丢弃,因为它被移动到那里。这不同于在语句末尾丢弃的效果。 - AetfOption<B>
。临时的 A 不应该被延长,因为它没有被借用。 - Aetf