以下 rust eclipse 代码compiles successfully:
struct StructNothing;
impl<'a> StructNothing {
fn nothing(&'a mut self) -> () {}
fn twice_nothing(&'a mut self) -> () {
self.nothing();
self.nothing();
}
}
然而,如果我们尝试将其打包到trait中,it fails:
pub trait TraitNothing<'a> {
fn nothing(&'a mut self) -> () {}
fn twice_nothing(&'a mut self) -> () {
self.nothing();
self.nothing();
}
}
这给我们:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:6:9
|
1 | pub trait TraitNothing<'a> {
| -- lifetime `'a` defined here
...
5 | self.nothing();
| --------------
| |
| first mutable borrow occurs here
| argument requires that `*self` is borrowed for `'a`
6 | self.nothing();
| ^^^^ second mutable borrow occurs here
- 为什么第一个版本是允许的,而第二个版本是禁止的?
- 有没有办法让编译器相信第二个版本是可以的?
背景和动机
像rust-csv
这样的库希望支持流、零拷贝解析,因为它比分配内存快25到50倍(根据基准测试). But Rust's built-in Iterator
trait can't be used for this,因为没有办法实现collect()
。目标是定义一个StreamingIterator
trait,它可以被rust-csv
和几个类似的库共享,但是迄今为止实现它的每一种尝试都遇到了上述问题。
4条答案
按热度按时间osh3o9ms1#
以下是弗朗西斯使用隐式生存期的答案的扩展,但它允许返回值受生存期限制:
它并不完美,但是你可以在其他方法中多次调用具有隐式生存期的方法
change_it
和nothing
。我不知道这是否能解决你真实的的问题,因为最终self
在trait方法中具有泛型类型&mut Self
,而在struct中具有类型&mut StructNothing
,并且编译器不能保证Self
不't包含引用。此解决方法确实解决了代码示例的问题。w3nuxt5m2#
如果你把生存期参数放在每个方法上,而不是trait本身上,it compiles:
mv1qrgav3#
似乎没有人回答“为什么”,所以我来了。
重点是:* 在trait中,我们调用来自同一trait的方法。然而,在free impl中,我们不调用来自同一impl的方法 。
什么?我们肯定从同一个impl调用方法吗?
让我们更准确地说:我们从相同的impl调用方法, 但不使用相同的泛型参数 *。
您的免费impl基本上等同于以下内容:
因为impl的泛型生存期是浮点的,所以可以为每个方法单独选择。编译器不调用
<Self<'a>>::nothing(self)
,而是调用<Self<'some_shorter_lifetime>>::nothing(&mut *self)
。另一方面,trait的情况完全不同,我们唯一能确定的是
Self: Trait<'b>
,我们不能用更短的生存期调用nothing()
,* 因为Self
可能没有实现更短生存期的Trait
*,因此,我们被迫调用<Self as Trait<'a>>::nothing(self)
,结果是我们在为重叠区域借用。由此我们可以推断出,如果我们告诉编译器
Self
在 * 任意 * 生存期内实现Trait
,它将工作:...除了因为issue #84435而无法编译,所以我不知道这是否会成功:(
z0qdvdin4#
这真的令人惊讶吗?
你所做的Assert是
&mut self
至少持续'a
的生存期。在前一种情况下,
&mut self
是指向结构体的指针。由于借位完全包含在nothing()
中,因此不会发生指针别名。在后一种情况下,
&mut self
是一个指针 * 指向一个指针 * 指向trait的一个结构体+一个vtable,你在'a
的持续时间内锁定了指向TraitNothing
的结构体;即每次执行整个函数。通过删除
'a
,您隐式地使用了'static
,这意味着impl将永久存在,所以这很好。如果您想解决这个问题,可以将
&'a TraitNothing
转换为&'static TraitNothing
......但我非常肯定这不是您想要做的。这就是为什么我们在Rust中需要块作用域(
'b: { .... }
)...Try using dummy lifetimes perhaps?