首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么将函数移到默认的特性方法会导致借用错误?

为什么将函数移到默认的特性方法会导致借用错误?
EN

Stack Overflow用户
提问于 2020-05-10 00:32:47
回答 2查看 86关注 0票数 1

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

代码语言:javascript
复制
#[derive(Debug)]
struct Foo {
    bar: Vec<i8>,
}

我编写了一个可变视图对象,用于封装Foo的一部分。

代码语言:javascript
复制
#[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] }

代码语言:javascript
复制
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提供了一个默认实现:

代码语言:javascript
复制
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中调用mutate

代码语言:javascript
复制
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作为特征上的默认方法来实现,会导致与借入检查器不同的行为?我怎样才能让这种特质发挥作用?

没有这种特质的例子。

以这种特质为例。

使用rustc版本1.43.1。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-05-10 04:05:51

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

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

这就提出了为什么非特质版本有效的问题。它不也是一样的吗?答案在于方差类型的std::slice::Iter<'a, T>std::slice::IterMut<'a, T>。泛型类型T<'a>的方差是T<'a>'a'b相关时是否和如何被胁迫到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>,以便以后可以发生可变的借用。

代码语言:javascript
复制
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];
    }
}

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

代码语言:javascript
复制
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>在其生命周期内是不变的。这是因为在一般情况下,可变的引用必须是不变的,才能成为声音。这意味着,如果您交换了可变和不可变借款的顺序,那么即使在非特征版本中也会出现错误。

代码语言:javascript
复制
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的每个实现中。当使用具体类型IterIterMut时,编译器知道我们可以使用协方差来缩短生命周期。

一个更有原则的解决方案是使Self::IterSelf::IterMut在其生命周期内具有通用性,以便可以根据需要缩短借款时间。像这样的通用关联类型还不可能。

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

代码语言:javascript
复制
#![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);
}

(游乐场)

票数 1
EN

Stack Overflow用户

发布于 2020-05-10 14:49:22

感谢SCappella的回答,我理解并解决了我的问题。由于我更希望有一个干净的代码库,而不是高效的用例,所以我用向量替换了迭代器:

代码语言:javascript
复制
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];
        }
    }
}
代码语言:javascript
复制
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方法。

游乐场

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61705663

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档