我正在处理一个可以简化为以下代码的问题.结果是error[E0499]: cannot borrow *Dog as mutable more than once at a time..

我很清楚这个错误的根源,但我想知道解决它的正确方法是什么? 请注意,WalkerSwimmer特征使用一个公共字段来减少剩余的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 {});
}

我找到的一种可能的解决方案是拆分borrow -这意味着向Dog struct 添加WalkerSwimmer个字段并将它们传递给函数,但共享状态使其变得复杂.

Edit

我猜在本例中,我可以使用我想要避免的RefCell,或者添加一个接受带有特征界限的泛型的新函数:

fn move_around<T: Walker + Swimmer>(entity: &mut T) {
    entity.walk();
    entity.swim();
}

推荐答案

您有几个选项:

  • 使用泛型(静态分派)并接受同时实现WalkerSwimmer的单个参数.

  • 与前面的动态分派类似-创建一个同时具有WalkerSwimmer作为超特征的新特征,为实现WalkerSwimmer的所有内容添加一个全面实现,并接受&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();
    }
    

    这并不排除对漫游者和游泳者使用不同的类型,只是需要为它们提供更多的样板:定义一个将它们都作为字段并将WalkerSwimmer转发给它们的 struct ,然后传递它.

  • 采用共享引用并使用内部可变性.有两种方法可以做到这一点.第一种方法是更改特征的方法,将共享&self作为参数,并将内部可变性(例如RefCell)放入 struct 中.第二种方法是按原样离开,但采取&RefCell<dyn Walker>种方法,对游泳运动员也是如此.每种方法都有优缺点.

    如果既不能更改方法,也不能更改特征(例如,因为它们来自外部库),则可以保持它们的原样,将RefCell放在 struct 中,实现特征for a shared reference to the struct(impl Walker for &'_ Dog),并将可变引用传递给共享引用:&mut &dog.

Rust相关问答推荐

值为可变对象的不可变HashMap

rust 迹-内存管理-POP所有权-链表

在不重写/专门化整个函数的情况下添加单个匹配手臂到特征的方法?

如何计算迭代器适配器链中过滤的元素的数量

为昂贵的for循环制作筛子

`RwLockWriteGuard_,T`不实现T实现的特征

获取与父字符串相关的&;str的原始片段

在铁 rust 中,如何一次只引用几件事中的一件?

获取已知数量的输入

我应该如何表达具有生命周期参数的类型的总排序,同时允许与不同生命周期进行比较?

Rust 中的静态引用

我可以用 Rust 编写一个不可变变量

在 Rust 中忽略 None 值的正确样式

我可以在 Rust 中 serde struct camel_case 和 deserde PascalCase

decltype、dyn、impl traits,重构时如何声明函数的返回类型

简单 TCP 服务器的连接由对等重置错误,mio 负载较小

改变不实现克隆的 dioxus UseState struct

Rust,使用枚举从 HashMap 获取值

通用类型,不同于输入类型,作为函数的返回值

list 中没有指定目标 - 必须存在 src/lib.rs、src/main.rs、[lib] 部分或 [[bin]] 部分