Rust结构可以两次借用“&'一个mut self”,那么为什么不能有特质呢?

时间:2014-10-04 11:55:52

标签: iterator rust lifetime

以下Rust代码compiles successfully

struct StructNothing;

impl<'a> StructNothing {
    fn nothing(&'a mut self) -> () {}

    fn twice_nothing(&'a mut self) -> () {
        self.nothing();
        self.nothing();
    }
}

但是,如果我们尝试将其打包成特征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特征,可以由rust-csv和几个类似的库共享,但到目前为止,每次尝试实现它都会遇到上述问题。

3 个答案:

答案 0 :(得分:2)

以下是使用隐式生命周期的弗朗西斯答案的扩展,但它允许返回值为终生限制:

pub trait TraitNothing<'a> {
    fn change_it(&mut self);

    fn nothing(&mut self) -> &Self {
        self.change_it();
        self
    }

    fn bounded_nothing(&'a mut self) -> &'a Self {
        self.nothing()
    }

    fn twice_nothing(&'a mut self) -> &'a Self {
        // uncomment to show old fail
        // self.bounded_nothing();
        // self.bounded_nothing()
        self.nothing();
        self.nothing()
    }
}

它不完美,但您可以在其他方法中多次使用隐式生命周期change_itnothing调用方法。我不知道这是否会解决您的真实问题,因为最终self在特征方法中具有泛型类型&mut Self,而在结构中它具有类型&mut StructNothing并且编译器可以& #39; t保证Self不包含引用。此解决方法确实解决了代码示例。

答案 1 :(得分:1)

如果您将生命周期参数放在每个方法而不是特征本身上,it compiles

pub trait TraitNothing {
    fn nothing<'a>(&'a mut self) -> () {}

    fn twice_nothing<'a>(&'a mut self) -> () {
        self.nothing();
        self.nothing();
    }
}

答案 2 :(得分:-2)

这真的令人惊讶吗?

您所做的断言是&mut self至少持续一生'a

在前一种情况下,&mut self是指向结构的指针。没有指针别名,因为借用完全包含在nothing()

在后一种情况下,&mut self是一个指针到指针到结构+一个vtable的特征。您在TraitNothing的持续时间内锁定了指向实现'a的结构的结构;即每次都是整个功能。

删除'a后,您隐式使用'static,表示impl会永久存在,所以很好。

如果你想解决这个问题,请将&'a TraitNothing转换为&'static TraitNothing ......但我很确定这不是你想要做的。

这就是我们在Rust ...

中需要块范围('b: { .... })的原因

Try using dummy lifetimes perhaps?