简体   繁体   English

Rust struct 可以两次借用“&'a mut self”,为什么 trait 不能?

[英]Rust struct can borrow "&'a mut self" twice, so why can't a trait?

The following Rust code compiles successfully :以下 Rust 代码编译成功

struct StructNothing;

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

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

However, if we try to package it in a trait, it fails :但是,如果我们尝试 package 它的特征, 它会失败

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

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

This gives us:这给了我们:

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
  • Why is the first version allowed, but the second version forbidden?为什么第一个版本是允许的,而第二个版本是禁止的?
  • Is there any way to convince the compiler that the second version is OK?有什么办法可以让编译器相信第二个版本没问题吗?

Background and motivation背景和动机

Libraries like rust-csv would like to support streaming, zero-copy parsing because it's 25 to 50 times faster than allocating memory (according to benchmarks).rust-csv这样的库希望支持流式、零拷贝解析,因为它比分配 memory(根据基准)快 25 到 50 倍。 But Rust's built-in Iterator trait can't be used for this , because there's no way to implement collect() . 但是 Rust 的内置Iterator特性不能用于此,因为没有办法实现collect() The goal is to define a StreamingIterator trait which can be shared by rust-csv and several similar libraries, but every attempt to implement it so far has run into the problem above.目标是定义一个StreamingIterator特征,它可以被rust-csv和几个类似的库共享,但到目前为止,每一次尝试实现它都遇到了上述问题。

The following is an extension of Francis's answer using implicit lifetimes but it allows for the return value to be lifetime bound: 以下是使用隐式生命周期的Francis答案的扩展,但它允许返回值为生命周期绑定:

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()
    }
}

It's less than perfect, but you can call the methods with implicit lifetimes change_it and nothing multiple times within other methods. 它不是完美的,但是您可以使用隐式生命周期change_it调用方法,而nothing在其他方法中多次调用。 I don't know if this will solve your real problem because ultimately self has the generic type &mut Self in the trait methods whereas in the struct it has type &mut StructNothing and the compiler can't guarantee that Self doesn't contain a reference. 我不知道这是否会解决你的真正问题,因为最终self在trait方法中具有泛型类型&mut Self ,而在struct中它具有类型&mut StructNothing ,并且编译器不能保证Self不包含引用。 This workaround does solve the code example. 此解决方法确实解决了代码示例。

If you put the lifetime parameters on each method rather than on the trait itself, it compiles : 如果将生命周期参数放在每个方法而不是特征本身上, 它将编译

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

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

Nobody seemed to answer the "why?"似乎没有人回答“为什么?” so here I am.所以我在这里。

Here's the point: In the trait, we're calling methods from the same trait.重点是:在特征中,我们从同一个特征调用方法。 However, in the free impl, we're not calling methods from the same impl .然而,在免费的 impl 中,我们并没有从同一个 impl 调用方法

What?什么? Surely we call methods from the same impl?我们肯定从同一个 impl 调用方法吗?

Let's be more precise: we're calling methods from the same impl, but not with the same generic parameters .更准确地说:我们从同一个 impl 调用方法,但不使用相同的通用参数

Your free impl is essentially equivalent to the following:您的 free impl 本质上等同于以下内容:

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

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

Because the impl's generic lifetime is floating, it can be chosen separately for each method.因为 impl 的泛型生命周期是浮动的,所以可以为每个方法单独选择。 The compiler does not call <Self<'a>>::nothing(self) , but rather it calls <Self<'some_shorter_lifetime>>::nothing(&mut *self) .编译器不调用<Self<'a>>::nothing(self) ,而是调用<Self<'some_shorter_lifetime>>::nothing(&mut *self)

With the trait, on the other hand, the situation is completely different.而有了特质,情况就完全不同了。 The only thing we can know for sure is that Self: Trait<'b> .我们唯一可以确定的是Self: Trait<'b> We cannot call nothing() with a shorter lifetime, because maybe Self doesn't implement Trait with the shorter lifetime .我们不能用更短的生命周期调用nothing()因为也许Self没有实现生命周期更短的Trait Therefore, we are forced to call <Self as Trait<'a>>::nothing(self) , and the result is that we're borrowing for overlapping regions.因此,我们不得不调用<Self as Trait<'a>>::nothing(self) ,结果是我们借用了重叠区域。

From this we can infer that if we tell the compiler that Self implements Trait for any lifetime it will work:由此我们可以推断,如果我们告诉编译器Self任何生命周期内实现Trait ,它将起作用:

fn twice_nothing(&'a mut self)
where
    Self: for<'b> TraitNothing<'b>,
{
    (&mut *self).nothing();
    (&mut *self).nothing();
}

...except it fails to compile because of issue #84435 , so I don't know whether this would have succeeded:( ...除了由于问题 #84435而无法编译,所以我不知道这是否会成功:(

Is this really surprising? 这真的很令人惊讶吗?

The assertion you're making is that &mut self lasts for at least the lifetime 'a . 你所做的断言是&mut self至少持续一生'a

In the former case, &mut self is a pointer to a struct. 在前一种情况下, &mut self是指向结构的指针。 No pointer aliasing occurs because the borrow is entirely contained in nothing() . 没有指针别名,因为借用完全包含在nothing()

In the latter case, the &mut self is a pointer to a pointer to a struct + a vtable for the trait. 在后一种情况下, &mut self是指向结构的指针的指针 +特征的vtable。 You're locking the pointed to struct that implements TraitNothing for the duration of 'a ; 你在'a ;期间锁定了指向实现TraitNothing struct的结构; ie the whole function each time. 即每次的整个功能。

By removing 'a , you're implicitly using 'static , which says the impl lasts forever, so its fine. 通过删除'a ,你隐含地使用'static ,这表示impl持续永久,所以它很好。

If you want to work around it, transmute the &'a TraitNothing to &'static TraitNothing ... but I'm pretty sure that's not what you want to do. 如果你想解决这个问题,可以将&'a TraitNothing&'static TraitNothing ......但我很确定这不是你想要做的。

This is why we need block scopes ( 'b: { .... } ) in Rust... 这就是我们在Rust中需要块范围( 'b: { .... } )的原因...

Try using dummy lifetimes perhaps? 尝试使用虚拟生命周期吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 可迭代、.iter(&amp;mut self) 和可变借用不可变数据 - Iterable, .iter(&mut self) and mutable borrow from immutable data 为什么我不能在两个不同的 map 函数中可变地借用一个变量? - Why can I not mutably borrow a variable in two different map functions? 为什么我需要在 ListIterator 中调用 previous() 两次才能进行“反向”迭代? - Why do I need to call previous() twice in ListIterator so I can go in 'reverse' iteration? 如何在Rust中的锁定结构成员上返回迭代器? - How can I return an iterator over a locked struct member in Rust? Rust 迭代器泛型问题,无法编译 - Rust iterator generics question, can't compile 为什么我不能在同一个迭代器上迭代两次? 如何“重置”迭代器或重用数据? - Why can't I iterate twice over the same iterator? How can I "reset" the iterator or reuse the data? 为什么不能两次反转迭代器以获得向量的最后两个数字? - Why can't I reverse an iterator twice to get the last two numbers of a vector? 为什么 Rust 中的 for 循环可以遍历切片或迭代器,而不是数组? - Why can a for loop in Rust iterate over a slice or iterator, but not an array? 为什么不能使用? (问号)直接在 Rust 中的 collect() 上? - Why can you not use ? (question mark) directly on collect() in Rust? 迭代器的生命周期:fn next(&&#39;a mut self) - Lifetime in Iterator: fn next(&'a mut self)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM