如何将实现多个特征的结构传递给接受这些特征作为 &mut 的函数?

问题描述 投票:0回答:1

我正在处理一个可以简化为以下代码的问题。结果是

error[E0499]: cannot borrow 
*狗
 as mutable more than once at a time.

错误的根源对我来说很清楚,但我想知道 - 解决它的正确方法是什么? 请注意,

Walker
Swimmer
特征使用公共字段来减少剩余的
energy
,所以对我来说,让
Dog
来实现这两个特征是有意义的。

我想保持功能

move_around
不变,因为步行和游泳的顺序对我来说很重要:)任何帮助将不胜感激。

trait Swimmer {
    fn swim(&mut self);
}

trait Walker {
    fn walk(&mut self);
}

struct Fish {}

impl Swimmer for Fish {
    fn swim(&mut self) {}
}

struct Turtle {}

impl Walker for Turtle {
    fn walk(&mut self) {}
}

struct Dog {
    energy: usize,
}

impl Swimmer for Dog {
    fn swim(&mut self) {
        self.energy -= 1;
    }
}

impl Walker for Dog {
    fn walk(&mut self) {
        self.energy -= 1;
    }
}

fn move_around(walker: &mut dyn Walker, swimmer: &mut dyn Swimmer) {
    walker.walk();
    swimmer.swim();
}

fn main() {
    // This does not compile: error[E0499]: cannot borrow `*dog` as mutable more than once at a time
    let dog = &mut Dog { energy: 100 };
    move_around(dog, dog);

    // Works fine.
    // move_around(&mut Turtle {}, &mut Fish {});
}

我发现的可能解决方案之一是拆分借用 - 这意味着将

Walker
Swimmer
字段添加到
Dog
结构并将它们传递给函数,但共享状态使其变得复杂。

编辑

我想在这种情况下,我可以使用我想避免的

RefCell
,或者添加一个接受具有特征边界的泛型的新函数:

fn move_around<T: Walker + Swimmer>(entity: &mut T) {
    entity.walk();
    entity.swim();
}
rust reference traits borrow-checker mutable
1个回答
0
投票

您有多种选择:

  • 使用泛型(静态调度)并采用单个参数来实现
    Walker
    Swimmer
  • 与前面的动态调度类似 - 创建一个新特征,将
    Walker
    Swimmer
    作为超级特征,为实现
    Walker
    Swimmer
    的所有内容添加全面实现,并接受
    &mut dyn WalkerAndSwimmer
    。那就是:
trait WalkerAndSwimmer: Walker + Swimmer {}
impl<T: ?Sized + Walker + Swimmer> WalkerAndSwimmer for T {}

fn move_around(v: &mut dyn WalkerAndSwimmer) {
    v.walk();
    v.swim();
}

这并不排除步行者和游泳者使用不同类型,只是需要为他们提供更多样板:定义一个结构体,将它们都作为字段并将

Walker
Swimmer
转发给他们,然后传递它。

  • 获取共享引用并使用内部可变性。有两种方法可以做到这一点。第一个是更改特征的方法以采用共享
    &self
    并将内部可变性(例如
    RefCell
    )放入结构内部。第二种是保持方法不变,但采用
    &RefCell<dyn Walker>
    ,游泳运动员也一样。每种方法都有优点和缺点。
© www.soinside.com 2019 - 2024. All rights reserved.