将 Rust 中的错误与特征对象生命周期混淆
Confusing error in Rust with trait object lifetime
谁能告诉我下面的代码有什么问题?编译器抱怨生命周期,但错误消息完全没有意义。我已经尝试了所有我能想到的方法,但似乎没有任何帮助。
use std::borrow::BorrowMut;
trait Trait<'a> {
fn accept(&mut self, &'a u8);
}
struct Impl<'a>{
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl{myref: None})
}
fn user<'a>(obj: &mut Trait<'a>) {}
fn parent<'a>(x: &'a u8) {
let mut pool = new();
user(pool.borrow_mut());
}
编译错误为
error: `pool` does not live long enough
--> src/wtf.rs:22:10
|
22 | user(pool.borrow_mut());
| ^^^^ does not live long enough
23 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
这完全没有意义。借款人如何活得更久?我什至没有使用借来的价值!
请注意,错误消息还有更多内容:
error: `pool` does not live long enough
--> src/main.rs:25:10
|>
25 |> user(pool.borrow_mut());
|> ^^^^
note: reference must be valid for the block at 23:25...
--> src/main.rs:23:26
|>
23 |> fn parent<'a>(x: &'a u8) {
|> ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
--> src/main.rs:24:26
|>
24 |> let mut pool = new();
|> ^
我们来看看user
:
fn user<'a>(obj: &mut Trait<'a>) {}
这表示它将接受对使用生命周期参数化的特征对象的可变引用(具有未命名的生命周期)'a
。
转向new
,我会说该方法高度可疑:
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
这表示它将 return 一个盒装特征对象 无论调用者指定什么生命周期 。 That basically never makes sense。
综上所述,我不清楚为什么代码选择使用 borrow_mut
。我会更直接地写:
user(&mut *pool);
这取消引用 Box<Trait>
以获得 Trait
,然后采用可变引用,产生 &mut Trait
,编译。
我目前无法解释为什么 BorrowMut
行为不同。
我不确定为什么会出现这个错误,但我可以给出解决方案!
首先,似乎使用 borrow_mut
不必要地限制了返回引用的生命周期。使用运算符创建引用解决了错误。
fn parent() {
let mut pool = new();
user(&mut *pool);
}
但是,如果我们不这样做,我们可以通过在 user
中添加绑定到 Trait
对象的生命周期来解决错误s obj
参数。
fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}
好的,这个确实有道理,但由于生命周期省略,很难看到。因此,这是您的代码,其中 所有 生命周期都明确写出,并剔除不相关的细节:
use std::borrow::BorrowMut;
trait Trait<'a> {}
struct Impl<'a> {
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}
fn parent() {
/* 'i: */ let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */ let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
/* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */ let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
user(pool_borrow);
}
现在,从parent
的最后一行来看,我们可以通过阅读user
的定义并代入我们在parent
中的生命周期来计算出以下等价]:
'a
= 'x
'b
= 'i
'b
= 'x
此外,这让我们得出结论:
'x
= 'i
这就是问题所在。由于您定义 user
的方式,您将自己置于 pool_ref
借用的生命周期(等于 pool
存储位置的生命周期're borrowing from) 必须与在 pool
.
中 存储 的事物中使用的生命周期 'x
相同
这有点像Box
在它存在之前能够有一个指向它自己的指针,这没有任何意义。
无论哪种方式,修复都很简单。将 user
更改为实际具有正确的类型:
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {}
这与 new
生成的类型匹配。或者,只是不要使用 borrow_mut
:
user(&mut *pool)
这个有效,因为它是"re-borrowing"。调用 borrow_mut
或多或少会直接转换生命周期,但重新借用允许编译器将借用范围缩小到更短的生命周期。换句话说,显式调用 borrow_mut
不允许编译器有足够的自由来 "fudge" 使它们全部排列的生命周期,重新借用 确实 .
顺便提一句:
I'm not even using the borrowed value!
不相关。 Rust 在本地完全 进行类型和生命周期检查。它从不查看另一个函数的主体以查看它在做什么;它单独出现在界面上。编译器既不检查也不关心你在内部一个不同的函数中做什么。
谁能告诉我下面的代码有什么问题?编译器抱怨生命周期,但错误消息完全没有意义。我已经尝试了所有我能想到的方法,但似乎没有任何帮助。
use std::borrow::BorrowMut;
trait Trait<'a> {
fn accept(&mut self, &'a u8);
}
struct Impl<'a>{
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl{myref: None})
}
fn user<'a>(obj: &mut Trait<'a>) {}
fn parent<'a>(x: &'a u8) {
let mut pool = new();
user(pool.borrow_mut());
}
编译错误为
error: `pool` does not live long enough
--> src/wtf.rs:22:10
|
22 | user(pool.borrow_mut());
| ^^^^ does not live long enough
23 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
这完全没有意义。借款人如何活得更久?我什至没有使用借来的价值!
请注意,错误消息还有更多内容:
error: `pool` does not live long enough
--> src/main.rs:25:10
|>
25 |> user(pool.borrow_mut());
|> ^^^^
note: reference must be valid for the block at 23:25...
--> src/main.rs:23:26
|>
23 |> fn parent<'a>(x: &'a u8) {
|> ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
--> src/main.rs:24:26
|>
24 |> let mut pool = new();
|> ^
我们来看看user
:
fn user<'a>(obj: &mut Trait<'a>) {}
这表示它将接受对使用生命周期参数化的特征对象的可变引用(具有未命名的生命周期)'a
。
转向new
,我会说该方法高度可疑:
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
这表示它将 return 一个盒装特征对象 无论调用者指定什么生命周期 。 That basically never makes sense。
综上所述,我不清楚为什么代码选择使用 borrow_mut
。我会更直接地写:
user(&mut *pool);
这取消引用 Box<Trait>
以获得 Trait
,然后采用可变引用,产生 &mut Trait
,编译。
我目前无法解释为什么 BorrowMut
行为不同。
我不确定为什么会出现这个错误,但我可以给出解决方案!
首先,似乎使用 borrow_mut
不必要地限制了返回引用的生命周期。使用运算符创建引用解决了错误。
fn parent() {
let mut pool = new();
user(&mut *pool);
}
但是,如果我们不这样做,我们可以通过在 user
中添加绑定到 Trait
对象的生命周期来解决错误s obj
参数。
fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}
好的,这个确实有道理,但由于生命周期省略,很难看到。因此,这是您的代码,其中 所有 生命周期都明确写出,并剔除不相关的细节:
use std::borrow::BorrowMut;
trait Trait<'a> {}
struct Impl<'a> {
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}
fn parent() {
/* 'i: */ let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */ let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
/* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */ let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
user(pool_borrow);
}
现在,从parent
的最后一行来看,我们可以通过阅读user
的定义并代入我们在parent
中的生命周期来计算出以下等价]:
'a
='x
'b
='i
'b
='x
此外,这让我们得出结论:
'x
='i
这就是问题所在。由于您定义 user
的方式,您将自己置于 pool_ref
借用的生命周期(等于 pool
存储位置的生命周期're borrowing from) 必须与在 pool
.
'x
相同
这有点像Box
在它存在之前能够有一个指向它自己的指针,这没有任何意义。
无论哪种方式,修复都很简单。将 user
更改为实际具有正确的类型:
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {}
这与 new
生成的类型匹配。或者,只是不要使用 borrow_mut
:
user(&mut *pool)
这个有效,因为它是"re-borrowing"。调用 borrow_mut
或多或少会直接转换生命周期,但重新借用允许编译器将借用范围缩小到更短的生命周期。换句话说,显式调用 borrow_mut
不允许编译器有足够的自由来 "fudge" 使它们全部排列的生命周期,重新借用 确实 .
顺便提一句:
I'm not even using the borrowed value!
不相关。 Rust 在本地完全 进行类型和生命周期检查。它从不查看另一个函数的主体以查看它在做什么;它单独出现在界面上。编译器既不检查也不关心你在内部一个不同的函数中做什么。