diff --git a/book/contents/fight-with-compiler/lifetime/loop.md b/book/contents/fight-with-compiler/lifetime/loop.md index a1495fb0..8ab73c94 100644 --- a/book/contents/fight-with-compiler/lifetime/loop.md +++ b/book/contents/fight-with-compiler/lifetime/loop.md @@ -127,13 +127,63 @@ fn random_empty_tile_2<'arr>(arr: &'arr mut [Tile]) -> &'arr mut Tile { 结果,编译器还是不给通过,报的错误几乎一样 ## 深层原因 -令人沮丧的是,我找遍了网上,也没有具体的原因,大家都说这是编译器太笨导致的问题,但是关于深层的原因,也没人能说出个 -所有然。 +令人沮丧的是,我找遍了网上,也没有具体的原因,大家都说这是编译器太笨导致的问题,但是关于深层的原因,也没人能说出个所有然。 因此,我无法在本文中给出为什么编译器会这么笨的真实原因,如果以后有结果,会在这里进行更新。 -## 解决办法 -虽然不能给出原因,但是我们可以看看解决办法,在上面,**移除中间变量**是一种办法,还有一种办法就是将部分引用移到循环外面. +------2022年1月13日更新------- +兄弟们,我带着挖掘出的一些内容回来了,再来看段错误代码先: +```rust +struct A { + a: i32 +} + +impl A { + fn one(&mut self) -> &i32{ + self.a = 10; + &self.a + } + fn two(&mut self) -> &i32 { + loop { + let k = self.one(); + if *k > 10i32 { + return k; + } + + // 可能存在的剩余代码 + // ... + } + } +} +``` + +我们来逐步深入分析下: + +1. 首先为`two`方法增加一下生命周期标识: `fn two<'a>(&'a mut self) -> &'a i32 { .. }`, 这里根据生命周期的[消除规则](../../advance/lifetime/basic.md#三条消除规则)添加的 +2. 根据生命周期标识可知:`two`中返回的`k`的生命周期必须是`'a` +3. 根据第2条,又可知:`let k = self.one();`中对`self`的借用生命周期也是`'a` +4. 因为`k`的借用发生在`loop`循环内,因此它需要小于等于循环的生命周期,但是根据之前的推断,它又要大于等于函数的生命周期`'a`,而函数的生命周期又大于等于循环生命周期, + +由上可以推出:`let k = self.one();`中`k`的生命周期要大于等于循环的生命周期,又要小于等于循环的生命周期, 唯一满足条件的就是:`k`的生命周期等于循环生命周期。 + +但是我们的`two`方法在循环中对`k`进行了提前返回,编译器自然会认为存在其它代码,这会导致`k`的生命周期小于循环的生命周期。 + +怎么办呢?很简单: +```rust +fn two(&mut self) -> &i32 { + loop { + let k = self.one(); + return k; + } +} +``` + +不要在`if`分支中返回`k`,而是直接返回,这样就让它们的生命周期相等了,最终可以顺利编译通过。 + +> 如果一个引用值从函数的某个路径提前返回了,那么该借用必须要在函数的所有返回路径都合法 + +## 解决方法 +虽然不能给出原因,但是我们可以看看解决办法,在上面,**移除中间变量**和**消除代码分支**都是可行的方法,还有一种方法就是将部分引用移到循环外面. #### 引用外移 ```rust