为什么将函数移动到默认特征方法会引入借用错误?

Why does moving a function to a default trait method introduce a borrowing error?

给定包含元素集合的结构 Foo

#[derive(Debug)]
struct Foo {
    bar: Vec<i8>,
}

我编写了一个可变视图对象,旨在封装 Foo:

的一部分
#[derive(Debug)]
struct View<'a> {
    foo: &'a mut Foo,
}

impl<'a> View<'a> {
    fn iter(&'a self) -> std::slice::Iter<'a, i8> {
        self.foo.bar.iter()
    }

    fn iter_mut(&'a mut self) -> std::slice::IterMut<'a, i8> {
        self.foo.bar.iter_mut()
    }

    fn mutate(&'a mut self) {
        let mut vector: Vec<i8> = vec![];
        for value in self.iter().take(1).cloned() {
            vector.push(value);
        }
        for value in self.iter_mut() {
            *value = 0;
        }
    }
}

上面的 View 结构按预期工作,下面的代码打印 Foo { bar: [0, 0, 0] }.

fn main() {
    let mut foo = Foo { bar: vec![0, 1, 2] };
    let mut view = View { foo: &mut foo };
    view.mutate();
    println!("{:?}", foo);
}

然而,不同类型的视图应该是可能的——如果 Foo 是一个矩阵,视图可以是行、列,甚至是子矩阵。因此,我将 View 重写为由结构实现的特征,并为 mutate 提供了默认实现:

trait AbstractView<'a> {
    type Iterator: Iterator<Item = &'a i8>;
    type IteratorMut: Iterator<Item = &'a mut i8>;

    fn iter(&'a self) -> Self::Iterator;
    fn iter_mut(&'a mut self) -> Self::IteratorMut;

    fn mutate(&'a mut self) {
        let mut vector: Vec<i8> = vec![];
        for value in self.iter().take(1).cloned() {
            vector.push(value);
        }
        for value in self.iter_mut() {
            *value = vector[0];
        }
    }
}

#[derive(Debug)]
struct View<'a> {
    foo: &'a mut Foo,
}

impl<'a> AbstractView<'a> for View<'a> {
    type Iterator = std::slice::Iter<'a, i8>;
    type IteratorMut = std::slice::IterMut<'a, i8>;

    fn iter(&'a self) -> Self::Iterator {
        self.foo.bar.iter()
    }

    fn iter_mut(&'a mut self) -> Self::IteratorMut {
        self.foo.bar.iter_mut()
    }
}

这段代码没有编译成功,rustc 抱怨 mutate 中对 iter_mut 的调用:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:22
   |
6  | trait AbstractView<'a> {
   |                    -- lifetime `'a` defined here
...
15 |         for value in self.iter().take(1).cloned() {
   |                      -----------
   |                      |
   |                      immutable borrow occurs here
   |                      argument requires that `*self` is borrowed for `'a`
...
18 |         for value in self.iter_mut() {
   |                      ^^^^^^^^^^^^^^^ mutable borrow occurs here

为什么将 mutate 实现为特征的默认方法会导致看起来与借用检查器不同的行为?我怎样才能让这个特性发挥作用?

Example without the trait.

Example with the trait.

使用 rustc 版本 1.43.1.

很容易说出为什么基于特征的版本不起作用,但很难说为什么原始 起作用。

都是在生生世世。对于基于特征的版本,到处只有一个生命周期 'a。当我们调用 self.iter()self.iter_mut() 时,借用将持续相同的生命周期。这意味着我们不能同时调用两者:如果我们同时调用两者,则不可变和可变借用具有相同的生命周期,因此它们同时存在。


这提出了非特征版本为何有效的问题。它不做完全相同的事情吗?答案在于 std::slice::Iter<'a, T>std::slice::IterMut<'a, T> 类型的 variance。泛型 T<'a> 的变体是当 'a'b 相关时,是否以及如何将 T<'a> 强制转换为 T<'b>

对于许多类型,这种关系是协变的:如果 'a'b 长(写作 'a: 'b),那么类型 T<'a> 的值可以被强制转换为值类型 T<'b>。对于其他一些类型,关系是逆变的:如果 'a: 'b,则 T<'b> 可以强制转换为 T<'a>(例如 Fn(&'a T))。最后,有些类型是不变的,因此不会发生强制转换。

std::slice::Iter<'a, T> 在生命周期 'a 中是协变的。如果 'a'b 长,我们可以强制缩短生命周期。这正是您的代码中发生的事情。当我们调用 self.iter().take(1).cloned() 时,self.iter() 实际上被强制转换为更短的 std::slice::Iter<'b, i8>,以便稍后可以发生可变借用。

fn mutate(&'a mut self) {
    let mut vector: Vec<i8> = vec![];
    // let iter = self.iter(); // works
    let mut iter: std::slice::Iter<'a, i8> = self.iter(); // doesn't work!
    for value in iter.take(1).cloned() {
        vector.push(value);
    }
    for value in self.iter_mut() {
        *value = vector[0];
    }
}

使用 the code above,我们得到一个类似于基于特征的代码的错误。

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:27:22
   |
11 | impl<'a> View<'a> {
   |      -- lifetime `'a` defined here
...
23 |         let iter: std::slice::Iter<'a, i8> = self.iter(); // doesn't work!
   |                   ------------------------   ---- immutable borrow occurs here
   |                   |
   |                   type annotation requires that `*self` is borrowed for `'a`
...
27 |         for value in self.iter_mut() {
   |                      ^^^^^^^^^^^^^^^ mutable borrow occurs here

顺便说一句,std::slice::IterMut<'a, T> 在其生命周期内是 不变的 。这是因为可变引用通常必须是不变的才能可靠。这意味着如果你交换可变和不可变借用的顺序,即使在非特征版本中你也会得到一个错误。

fn mutate(&'a mut self) {
    let mut vector: Vec<i8> = vec![];
    for value in self.iter_mut() {
        // This would panic if it compiled, of course
        *value = vector[0];
    }
    for value in self.iter().take(1).cloned() {
        vector.push(value);
    }
}

(playground)


因此基于特征的版本不起作用,因为self.iter() 要求借用持续时间太长并且 它不能被强制为更短的借用。事实上,按照事物的书写方式,更短的借用甚至可能没有意义。 Self::Iter 可能只针对那个特定的生命周期定义。

那么写这个的理想方式是什么?一种方法是将 mutate 的实现放在 AbstractView 的每个实现中。当使用具体类型 IterIterMut 时,编译器知道我们可以使用协变来缩短生命周期。

一个更有原则的解决方案是使 Self::IterSelf::IterMut 在它们的生命周期中通用,以便可以根据需要缩短借用。 Generic associated types这样的还不可能。

在夜间编译器上,可以执行此操作,但正如编译器正确警告的那样,泛型关联类型尚未完成,可能会导致编译器崩溃或错误。

#![feature(generic_associated_types)]

#[derive(Debug)]
struct Foo {
    bar: Vec<i8>,
}

trait AbstractView {
    type Iterator<'b>: Iterator<Item = &'b i8>;
    type IteratorMut<'b>: Iterator<Item = &'b mut i8>;

    // Eventually, these lifetimes should be elided
    // But it doesn't seem that that's implemented yet
    fn iter<'a>(&'a self) -> Self::Iterator<'a>;
    fn iter_mut<'a>(&'a mut self) -> Self::IteratorMut<'a>;

    fn mutate(&mut self) {
        let mut vector: Vec<i8> = vec![];
        for value in self.iter().take(1).cloned() {
            vector.push(value);
        }
        for value in self.iter_mut() {
            *value = vector[0];
        }
    }
}

#[derive(Debug)]
struct View<'a> {
    foo: &'a mut Foo,
}

impl<'a> AbstractView for View<'a> {
    type Iterator<'b> = std::slice::Iter<'b, i8>;
    type IteratorMut<'b> = std::slice::IterMut<'b, i8>;

    fn iter<'b>(&'b self) -> Self::Iterator<'b> {
        self.foo.bar.iter()
    }

    fn iter_mut<'b>(&'b mut self) -> Self::IteratorMut<'b> {
        self.foo.bar.iter_mut()
    }
}

fn main() {
    let mut foo = Foo { bar: vec![0, 1, 2] };
    let mut view = View { foo: &mut foo };
    view.mutate();
    println!("{:?}", foo);
}

(playground)

感谢 ,我已经了解并解决了我的问题。由于我宁愿有一个干净的代码库而不是一个高效的代码库用于我的用例,我用向量替换了迭代器:

trait AbstractView {
    fn refs(&self) -> Vec<&i8>;
    fn refs_mut(&mut self) -> Vec<&mut i8>;

    fn mutate(&mut self) {
        let mut vector: Vec<i8> = vec![];
        for value in self.refs().iter().take(1) {
            vector.push(**value);
        }
        for value in self.refs_mut() {
            *value = vector[0];
        }
    }
}
impl AbstractView for View<'_>
{
    fn refs(&self) -> Vec<&i8> {
        self.foo.bar.iter().collect()
    }

    fn refs_mut(&mut self) -> Vec<&mut i8> {
        self.foo.bar.iter_mut().collect()
    }
}

这样我就不会重复 mutate 方法。

Playground