首页 > 解决方案 > 为什么将函数移动到默认特征方法会引入借用错误?

问题描述

给定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 抱怨对iter_mutin的调用mutate

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在 trait 上实现为默认方法会导致看起来与借用检查器不同的行为?我怎样才能让这个特性发挥作用?

没有特征的例子。

具有特征的示例。

使用 rustc 版本 1.43.1。

标签: rusttraitslifetimeborrow-checker

解决方案


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

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


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

对于许多类型,这种关系是协变的:如果'a'b(书面'a: 'b)更长,那么 type 的值T<'a>可以强制转换为 type 的值T<'b>。对于其他一些类型,这种关系是逆变的: if 'a: 'b, thenT<'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];
    }
}

使用上面的代码,我们会得到一个类似于基于特征的代码的错误。

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>内是不变的。这是因为可变引用通常必须是不变的才能正确。这意味着如果你交换了可变借用和不可变借用的顺序,即使在非 trait 版本中也会出现错误。

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

(操场)


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

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

一个更有原则的解决方案是在它们的生命周期中使用 makeSelf::IterSelf::IterMutgeneric ,以便可以根据需要缩短借用时间。像这样的通用关联类型还不可能。

在 nightly 编译器上,可以这样做,但正如编译器正确警告的那样,泛型关联类型尚未完成,可能会导致编译器崩溃或错误。

#![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);
}

(操场)


推荐阅读