next
的签名在输入和输出的生命周期之间建立 _no_ 约束! 我们为什么要关心? 这意味着我们可以无条件地一遍又一遍地调用next
!
我觉得这里有逻辑上的差距。 IIUC 生命周期是为了限制输出相对于输入的范围,它如何阻止next()
被连续调用并不直观。
的具体例子
fn next<'b>(&'b mut self) -> Option<&'a T> { /* stuff */ }
将直接导致Option<&'a T>
与T
Option<&'a T>
存在的时间一样长的结论,直到该元素被drop()
ed 或整个列表都已死。 与Option<&'b T>
的主要区别在于元素是drop()
ed 时。
当next()
第二次(和第三次)被调用时会发生什么?
您是在提问还是希望文章包含更多说明? 换句话说,您是不确定这里的意思,还是您已经知道并且只是想扩展这篇文章? 我不完全确定。 :微笑:
@jonastepe
两者都有。 我在 OP 中只能走这么远,无法从生命周期限制到函数调用限制。 我认为需要澄清,但我只知道其中的一部分。
也许我应该先重新发布我之前删除的帖子......这对你有帮助吗?
IIUC 生命周期旨在限制输出相对于输入的范围,...
这不是指定生命周期的唯一目的。 主要是将函数/方法调用或复杂类型中的 _borrows_ 与另一个相关联。 您将您的意图传达给编译器,因此它可以根据您的意图进行推理,并决定这些意图是否符合其安全规则。
在此处提供的脱糖示例中:
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next<'b>(&'b mut self) -> Option<&'a T> { /* stuff */ }
}
我们可以看到self
的借用没有链接到返回的引用。 生命周期的命名不同,它们没有任何关系。
self
的借用发生在我们调用next()
,但它不会通过将返回的引用存储在变量中来扩展,如下所示:
let x = iter.next().unwrap();
返回的引用具有生命周期'a
而不是'b
。 因此, self
的借用只持续到调用next()
那一刻。 因此,我随后可以再次调用next()
,因为self
的可变借用只持续了那一刻。
让我们假设它会持续更长时间,并且方法签名看起来像这样(注意,你不能用今天的 Rust 做到这一点,因为 Iterator trait 指定了上面的签名):
fn next<'a>(&'a mut self) -> Option<&'a T>;
现在我向编译器传达,我希望将两个生命周期/借用链接起来。 因此,如果我调用next()
并将返回的引用/借用存储在一个变量中,那么只要该变量在范围内,就会将self
的借用扩展到持续时间。 然后我不能这样做:
let x = iter.next().unwrap();
let y = iter.next().unwrap(); // error, more than one mutable borrow of iter!
到我第二次调用next()
时, x
仍在范围内,因此iter
的可变借用(即self
next()
) 的签名也在范围内。 你可以看到这将是非常有限的。 在我们可以继续调用next()
之前, x
必须超出范围。
这也是不必要的。 Iter
借用了一个 T(或一些 T 的集合),它不拥有T,所以我不是从Iter
(上面的第二个例子传达给编译器)借用的。 我向 T 借款。 Iter
只是 T 的另一个借款人,我正在用它来获得 T 的另一个借款。在此过程中可变地借入Iter
完全是矫枉过正。
作者@Gankro在这里所做的是设置舞台,以强调与IterMut
的区别。 那个返回可变引用( &mut T
)。 使用Iter
可以获得对基础数据的多个共享引用。 这对于共享引用来说是微不足道的; 范围内可以有多个。 令人惊奇的是,这对于可变引用也是可能的,只要您从中借用的底层数据结构轻松分区为 _disjoint_ 子结构。
@jonastepe
非常感谢! 我怀疑赋值和&mut
会共同阻止第二次函数调用,但未能将生命周期融入这个难题中。 我非常希望将您的解释添加到书中。 可以提交PR吗? 如果不是,我可以吗?
当然你可能:眨眼:。 我认为这本书目前需要更多的内容和解释。 Rust 中的数据结构因其所有权规则而成为一种特殊的话题。 我认为将来我们也应该为其他数据结构提供实现示例。
最有用的评论
也许我应该先重新发布我之前删除的帖子......这对你有帮助吗?
这不是指定生命周期的唯一目的。 主要是将函数/方法调用或复杂类型中的 _borrows_ 与另一个相关联。 您将您的意图传达给编译器,因此它可以根据您的意图进行推理,并决定这些意图是否符合其安全规则。
在此处提供的脱糖示例中:
我们可以看到
self
的借用没有链接到返回的引用。 生命周期的命名不同,它们没有任何关系。self
的借用发生在我们调用next()
,但它不会通过将返回的引用存储在变量中来扩展,如下所示:返回的引用具有生命周期
'a
而不是'b
。 因此,self
的借用只持续到调用next()
那一刻。 因此,我随后可以再次调用next()
,因为self
的可变借用只持续了那一刻。让我们假设它会持续更长时间,并且方法签名看起来像这样(注意,你不能用今天的 Rust 做到这一点,因为 Iterator trait 指定了上面的签名):
现在我向编译器传达,我希望将两个生命周期/借用链接起来。 因此,如果我调用
next()
并将返回的引用/借用存储在一个变量中,那么只要该变量在范围内,就会将self
的借用扩展到持续时间。 然后我不能这样做:到我第二次调用
next()
时,x
仍在范围内,因此iter
的可变借用(即self
next()
) 的签名也在范围内。 你可以看到这将是非常有限的。 在我们可以继续调用next()
之前,x
必须超出范围。这也是不必要的。
Iter
借用了一个 T(或一些 T 的集合),它不拥有T,所以我不是从Iter
(上面的第二个例子传达给编译器)借用的。 我向 T 借款。Iter
只是 T 的另一个借款人,我正在用它来获得 T 的另一个借款。在此过程中可变地借入Iter
完全是矫枉过正。作者@Gankro在这里所做的是设置舞台,以强调与
IterMut
的区别。 那个返回可变引用(&mut T
)。 使用Iter
可以获得对基础数据的多个共享引用。 这对于共享引用来说是微不足道的; 范围内可以有多个。 令人惊奇的是,这对于可变引用也是可能的,只要您从中借用的底层数据结构轻松分区为 _disjoint_ 子结构。