谁借用了一个变量?
Who borrowed a variable?
我在和借阅检查员吵架。我有两段相似的代码,一段按我的预期工作,另一段没有。
符合我预期的那个:
mod case1 {
struct Foo {}
struct Bar1 {
x: Foo,
}
impl Bar1 {
fn f<'a>(&'a mut self) -> &'a Foo {
&self.x
}
}
// only for example
fn f1() {
let mut bar = Bar1 { x: Foo {} };
let y = bar.f(); // (1) 'bar' is borrowed by 'y'
let z = bar.f(); // error (as expected) : cannot borrow `bar` as mutable more
// than once at a time [E0499]
}
fn f2() {
let mut bar = Bar1 { x: Foo {} };
bar.f(); // (2) 'bar' is not borrowed after the call
let z = bar.f(); // ok (as expected)
}
}
没有的:
mod case2 {
struct Foo {}
struct Bar2<'b> {
x: &'b Foo,
}
impl<'b> Bar2<'b> {
fn f(&'b mut self) -> &'b Foo {
self.x
}
}
fn f4() {
let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
bar2.f(); // (3) 'bar2' is borrowed as mutable, but who borrowed it?
let z = bar2.f(); // error: cannot borrow `bar2` as mutable more than once at a time [E0499]
}
}
我希望我可以调用 Bar2::f
两次而不会激怒编译器,就像情况 1 一样。
问题在评论(3):谁借了bar2
,而没有做作?
这是我的理解:
情况1,f2
调用:lifetime参数'a
是接收&Foo
值的一个,所以当有时这个lifetime为空不做作,Bar1::f
调用后bar
不借用;
在情况 2 中,bar2
借用了 foo
(因为不可变),因此 Bar2
结构中的生命周期参数 'b
是 foo
引用生命周期,在 f4
主体结束时结束。调用 Bar2::f
在该生命周期内借用 bar2
,即到 f4
.
结束
但问题仍然是:谁借了bar2
?难道是Bar2::f
? Bar2::f
调用后如何持有借用的所有权?我在这里错过了什么?
我在 x86_64-pc-windows-msvc.
使用 Rust 1.14.0-nightly (86affcdf6 2016-09-28)
我将 f4()
的主体放在 main()
中并为 Bar2
实施 Drop
以查明它何时被删除(即超出范围):
impl<'b> Drop for Bar2<'b> {
fn drop(&mut self) { println!("dropping Bar2!"); }
}
结果是:
error: `bar2` does not live long enough
--> <anon>:24:5
|
24 | bar2.f();
| ^^^^ does not live long enough
25 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
有点可疑;让我们使用辅助作用域详细检查它:
fn main() {
{
let foo = Foo {}; // foo scope begins
{
let mut bar2 = Bar2 { x: &foo }; // bar2 scope begins; bar2 borrows foo
bar2.f();
} // bar2 should be dropped here, but it has the same lifetime as foo, which is still live
} // foo is dropped (its scope ends)
}
在我看来,这里存在漏洞,bar2
从未被删除(因此 Drop
无法实现)。这就是为什么你不能重新借用它。
在案例 #2 中,你有这个:
impl<'b> Bar2<'b> {
fn f(&'b mut self) -> &'b Foo {
self.x
}
}
要突出显示:&'b mut self
和 &'b Foo
指定了相同的生命周期。
这表示对 self
的引用和返回的对 Foo
实例的引用都具有相同的生命周期。查看调用站点,你有这个:
let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
因此编译器推断 foo
和 bar2
具有相同的生命周期。 foo
的生命周期是 f4
函数的范围,因此对 bar2
的可变引用共享此。
解决此问题的一种方法是删除 self
引用的显式生命周期:
fn f(&mut self) -> &'b Foo
这会编译并且编译器正确理解对 bar2
的引用和对 foo
的引用具有不同的生命周期。
游乐场:https://play.rust-lang.org/?gist=caf262dd628cf14cc2884a3af842276a&version=stable&backtrace=0
TLDR:是的,在 self 引用和返回的引用上具有相同的生命周期说明符意味着 f4
的整个范围持有 bar2
.
的可变借用
啊...你基本上是自借的
这个问题取决于您有相同的生命周期 ('b
) 用于 Foo
的生命周期和 Bar
的生命周期。然后编译器尽职尽责地统一了这些生命周期,你最终会遇到一种奇怪的情况,即本应在语句末尾结束的借用生命周期突然在值应该超出范围之后结束。
根据经验:始终 使用新的生命周期 self
。其他的都很奇怪。
有趣的是,这种模式实际上很有用(尽管更可能是不可变借用):它允许锚定一个值到堆栈帧,防止之后的任何移动对函数的调用,这(有时)可用于表示 Rust 未很好建模的借用(例如将指向值的指针传递给 FFI)。
我想补充一下 subtyping/variance 在这里扮演的角色。
&mut T
在 T
上是不变的。给定两个类型 T
和 U
,其中 T
< U
(T
是 U
的子类型),那么 &mut T
与 &mut U
没有子类型关系(即它们彼此不变),而 &T
是 &U
的子类型(&T
< &U
)。但是 &'lifetime
和 &'lifetime mut
都在 'lifetime
上是协变的。因此,给定类型 T
的两个生命周期 'a
和 'b
,其中 'a
比 'b
长,然后根据子类型关系 &'a T
< &'b T
,类似地 &'a mut T
< &'b mut T
问题来了,在函数 f
的调用中,self
是对 Bar2<'a>
的引用。编译器将查看它是否可以 "temporarily shorten" bar2
的生命周期来适应函数 f
的调用范围 'x
,就好像 bar2
foo
是在调用 f
之前创建的,并在 f
之后立即消失(即临时缩短:假设变量 bar2
在 'x
中创建,因此 Bar2<'a>
到 Bar2<'x>
,'a
是原始(实际)生命周期)。但是在这里,"shortening"是不可能的;一是因为对 self
的可变引用,二是在函数 f
中对 Foo
和 Bar2
(self
) 的引用具有相同的生命周期定义。首先,由于它是一个可变引用,它不能将Bar2<'a>
转换为Bar2<'x>
,因为&mut Bar2<'a>
和&mut Bar2<'x>
是相互不变的。 (请记住,即使 T < U
或 T > U
,那么 &mut T
对 &mut U
也是不变的)。因此编译器必须使用 Bar2<'a>
,其次,由于函数 f
对 Bar2
和 Foo
的引用具有相同的生命周期,因此无法转换 &'a Bar2<'a>
到 &'x Bar2<'a>
。所以这意味着在调用函数 f
时引用不是 "shortened" 并且它们将保持有效直到块结束。
如果 self
的生命周期被省略,那么编译器会给 self
一个新的生命周期(与 'b
不相交),这意味着它可以自由地 "temporarily shorten" Bar2
的生命,然后将它的 mut
引用传递给 f
。即它将执行 &'a mut Bar2<'a>
到 &'x mut Bar2<'a>
,然后将其传递给 f
。 (记住 &'lifetime mut
在 'lifetime
上是协变的)因此它会起作用。
我在和借阅检查员吵架。我有两段相似的代码,一段按我的预期工作,另一段没有。
符合我预期的那个:
mod case1 {
struct Foo {}
struct Bar1 {
x: Foo,
}
impl Bar1 {
fn f<'a>(&'a mut self) -> &'a Foo {
&self.x
}
}
// only for example
fn f1() {
let mut bar = Bar1 { x: Foo {} };
let y = bar.f(); // (1) 'bar' is borrowed by 'y'
let z = bar.f(); // error (as expected) : cannot borrow `bar` as mutable more
// than once at a time [E0499]
}
fn f2() {
let mut bar = Bar1 { x: Foo {} };
bar.f(); // (2) 'bar' is not borrowed after the call
let z = bar.f(); // ok (as expected)
}
}
没有的:
mod case2 {
struct Foo {}
struct Bar2<'b> {
x: &'b Foo,
}
impl<'b> Bar2<'b> {
fn f(&'b mut self) -> &'b Foo {
self.x
}
}
fn f4() {
let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
bar2.f(); // (3) 'bar2' is borrowed as mutable, but who borrowed it?
let z = bar2.f(); // error: cannot borrow `bar2` as mutable more than once at a time [E0499]
}
}
我希望我可以调用 Bar2::f
两次而不会激怒编译器,就像情况 1 一样。
问题在评论(3):谁借了bar2
,而没有做作?
这是我的理解:
情况1,
f2
调用:lifetime参数'a
是接收&Foo
值的一个,所以当有时这个lifetime为空不做作,Bar1::f
调用后bar
不借用;在情况 2 中,
bar2
借用了foo
(因为不可变),因此Bar2
结构中的生命周期参数'b
是foo
引用生命周期,在f4
主体结束时结束。调用Bar2::f
在该生命周期内借用bar2
,即到f4
. 结束
但问题仍然是:谁借了bar2
?难道是Bar2::f
? Bar2::f
调用后如何持有借用的所有权?我在这里错过了什么?
我在 x86_64-pc-windows-msvc.
使用 Rust 1.14.0-nightly (86affcdf6 2016-09-28)我将 f4()
的主体放在 main()
中并为 Bar2
实施 Drop
以查明它何时被删除(即超出范围):
impl<'b> Drop for Bar2<'b> {
fn drop(&mut self) { println!("dropping Bar2!"); }
}
结果是:
error: `bar2` does not live long enough
--> <anon>:24:5
|
24 | bar2.f();
| ^^^^ does not live long enough
25 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
有点可疑;让我们使用辅助作用域详细检查它:
fn main() {
{
let foo = Foo {}; // foo scope begins
{
let mut bar2 = Bar2 { x: &foo }; // bar2 scope begins; bar2 borrows foo
bar2.f();
} // bar2 should be dropped here, but it has the same lifetime as foo, which is still live
} // foo is dropped (its scope ends)
}
在我看来,这里存在漏洞,bar2
从未被删除(因此 Drop
无法实现)。这就是为什么你不能重新借用它。
在案例 #2 中,你有这个:
impl<'b> Bar2<'b> {
fn f(&'b mut self) -> &'b Foo {
self.x
}
}
要突出显示:&'b mut self
和 &'b Foo
指定了相同的生命周期。
这表示对 self
的引用和返回的对 Foo
实例的引用都具有相同的生命周期。查看调用站点,你有这个:
let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
因此编译器推断 foo
和 bar2
具有相同的生命周期。 foo
的生命周期是 f4
函数的范围,因此对 bar2
的可变引用共享此。
解决此问题的一种方法是删除 self
引用的显式生命周期:
fn f(&mut self) -> &'b Foo
这会编译并且编译器正确理解对 bar2
的引用和对 foo
的引用具有不同的生命周期。
游乐场:https://play.rust-lang.org/?gist=caf262dd628cf14cc2884a3af842276a&version=stable&backtrace=0
TLDR:是的,在 self 引用和返回的引用上具有相同的生命周期说明符意味着 f4
的整个范围持有 bar2
.
啊...你基本上是自借的
这个问题取决于您有相同的生命周期 ('b
) 用于 Foo
的生命周期和 Bar
的生命周期。然后编译器尽职尽责地统一了这些生命周期,你最终会遇到一种奇怪的情况,即本应在语句末尾结束的借用生命周期突然在值应该超出范围之后结束。
根据经验:始终 使用新的生命周期 self
。其他的都很奇怪。
有趣的是,这种模式实际上很有用(尽管更可能是不可变借用):它允许锚定一个值到堆栈帧,防止之后的任何移动对函数的调用,这(有时)可用于表示 Rust 未很好建模的借用(例如将指向值的指针传递给 FFI)。
我想补充一下 subtyping/variance 在这里扮演的角色。
&mut T
在 T
上是不变的。给定两个类型 T
和 U
,其中 T
< U
(T
是 U
的子类型),那么 &mut T
与 &mut U
没有子类型关系(即它们彼此不变),而 &T
是 &U
的子类型(&T
< &U
)。但是 &'lifetime
和 &'lifetime mut
都在 'lifetime
上是协变的。因此,给定类型 T
的两个生命周期 'a
和 'b
,其中 'a
比 'b
长,然后根据子类型关系 &'a T
< &'b T
,类似地 &'a mut T
< &'b mut T
问题来了,在函数 f
的调用中,self
是对 Bar2<'a>
的引用。编译器将查看它是否可以 "temporarily shorten" bar2
的生命周期来适应函数 f
的调用范围 'x
,就好像 bar2
foo
是在调用 f
之前创建的,并在 f
之后立即消失(即临时缩短:假设变量 bar2
在 'x
中创建,因此 Bar2<'a>
到 Bar2<'x>
,'a
是原始(实际)生命周期)。但是在这里,"shortening"是不可能的;一是因为对 self
的可变引用,二是在函数 f
中对 Foo
和 Bar2
(self
) 的引用具有相同的生命周期定义。首先,由于它是一个可变引用,它不能将Bar2<'a>
转换为Bar2<'x>
,因为&mut Bar2<'a>
和&mut Bar2<'x>
是相互不变的。 (请记住,即使 T < U
或 T > U
,那么 &mut T
对 &mut U
也是不变的)。因此编译器必须使用 Bar2<'a>
,其次,由于函数 f
对 Bar2
和 Foo
的引用具有相同的生命周期,因此无法转换 &'a Bar2<'a>
到 &'x Bar2<'a>
。所以这意味着在调用函数 f
时引用不是 "shortened" 并且它们将保持有效直到块结束。
如果 self
的生命周期被省略,那么编译器会给 self
一个新的生命周期(与 'b
不相交),这意味着它可以自由地 "temporarily shorten" Bar2
的生命,然后将它的 mut
引用传递给 f
。即它将执行 &'a mut Bar2<'a>
到 &'x mut Bar2<'a>
,然后将其传递给 f
。 (记住 &'lifetime mut
在 'lifetime
上是协变的)因此它会起作用。