rfc 2049-nll中,Reborrow constraints 节 的 Example 3,
let foo = Foo { ... };
let p: &'p mut Foo = &mut foo;
let q: &'q mut &'p mut Foo = &mut p;
let r: &'r mut Foo = &mut **q;
use(*p); // <-- This line should result in an ERROR
use(r);
对于为什么 r 的使用 需要 extend p 的 life , 我的理解是,r 重借用 的对象 foo 的原始借用是 p,r是 (隐式)间接 借用 foo (r是借用的 **q,而不是foo,尽管逻辑上是)。r需要p来表达自己 对 foo 的借用,所以 r live 的地方,p 就需要存在。 但是 为什么 q 的 life 也需要 extend ?
文中对这个的 解释,有点看不懂。
"This use of r must extend the lifetime of the borrows used to create both p and q. Otherwise, one could access (and mutate) the same memory through both *r and *p. (In fact, the real rustc did in its early days have a soundness bug much like this one.)"。
这句话是说,同时存在两种 可变借用 可以修改引用对象的途径 吗。可是 extend p 的 life ,不是依然是有两种修改引用对象的途径吗。
"When we create r by borrowing **q, that is the last direct use of q -- so you might think we can release the lock on p, since q is no longer in (direct) use. However, that would be unsound, since then r and *p could both be used to access the same memory. The key is to recognize that r represents an indirect use of q (and q in turn is an indirect use of p), and hence so long as r is in use, p and q must also be considered "in use" (and hence their "locks" still enforced)."
"since then r and *p could both be used to access the same memory" 是对 "*r and *p" 的笔误吗。
另外,当last direct use of q 后,如果 释放p上的锁,也只是和p本身的访问修改有关。这和 “用 *r ,*p 可以访问同一内存” 有什么关系。
评论区
写评论嗯,明白了,不胜感谢!!
👇
苦瓜小仔: ```text
我只能解释到这了,看不懂别找我。。。
我只能解释到这了,看不懂别找我。。。
这个“栈”只是模型抽象。。。不是实际内存相关的栈。每次可用的独占借用,只能从一堆的最上面用,你要拿堆中间的,必须把其上的独占借用移除(弹出栈)才行。
嗯,我说的堆不是内存相关的堆,那只是一个量词。。。一堆碟子你能直接从中间拿一个吗?
这还用问吗。。。p 和 q 对不同的值独占借用,那分析有什么意义???谁也不打扰谁啊。
我这里只列的独占借用存在的情况。。。
如果在独占的栈借用存在时,使用共享借用,则把这个借用栈上的所有独占借用移除,把共享借用放置于栈上,所以这意味着在这个共享借用之前的独占借用都不再可用。
在一个堆栈里的是 同一个内存位置的吧,q 也算 借用 foo 的吗?不管经过多少层解引用,最终能访问那个位置,都算在一个堆栈里,是吧
--
👇
苦瓜小仔: 这是我现在的心智模型(结合栈借用)
这是我现在的心智模型(结合栈借用)
注意:栈借用与 NLL 并无实际联系,栈借用是对现有 Rust 借用行为的建模方式,而 NLL 是当前 Rust 借用的实现。
针对独占借用:
栈底 -> 栈1 -> 栈2 -> ... -> 栈顶
其中栈顶的独占借用才是真正能用的(或者说能被显式使用的,显式使用指在源代码中使用)
[^q]: 所以你可以在 (6) 写
use_foo_exclusively(q)
。但写use_foo_exclusively(p)
表示把 p 放置于栈顶(因为你正在使用它),从而 q 从栈上弹出,不能再被使用。同理,这也是那行“别这样用的”原因。@ zhylmzr ,@苦瓜小仔 。大佬, 能指点下不
👇
leolee0101: 使用 *p 不仅需要访问p本身,还需要p有访问foo的能力。此时,r借走了p的可变访问能力,即使p上没有锁,不是 也不能使用 *p,吗
👇
zhylmzr:
使用 *p 不仅需要访问p本身,还需要p有访问foo的能力。此时,r借走了p的可变访问能力,即使p上没有锁,不是 也不能使用 *p,吗
👇
zhylmzr:
嗯,之前在理解的时候,有个误区,认为 可变借用 p 在life 内 都拥有 可变访问权力。但其实 r 已经借走了p的可变访问能力,此时,p尽管 life 延长了,其实并没有 可变访问能力。
👇 zhylmzr:
怎么有两种修改引用的途径?Rust的各种规则就是为了避免数据竞争。
let r: &'r mut Foo = &mut **q;
可以理解这段代码会依次枚举 q, *q, **q, 如果其中有引用生命周期的话,那么就会扩大它们的生命周期(原因在于r使用的时候,它引用的生命周期一定不能失效),也就是 'r: 'q: 'p
怎么有两种修改引用的途径?Rust的各种规则就是为了避免数据竞争。
应该是的,r, p 都是指向 foo 的指针。
如果有锁的话就无法访问p了,释放了锁就可以通过 *p 和 *r 访问同一块也就是 foo 的内存了
感谢 大佬的详细示例,马上去学习理解。
--
👇
苦瓜小仔: 你只要遵照下面的代码示例来理解和使用独占引用、共享引用就好了
你只要遵照下面的代码示例来理解和使用独占引用、共享引用就好了
取消注释的话:
如果还想了解的话,看完 NLL 就去看栈借用 (stack borrows) 吧。