提问人:PWolf 提问时间:10/13/2023 最后编辑:PWolf 更新时间:10/13/2023 访问量:60
如何将实现多个特征的结构传递给接受这些特征为&mut的函数?
How to pass a struct implementing multiple traits to a function accepting those traits as &mut?
问:
我正在处理一个问题,可以简化为以下代码。结果是 *dog。error[E0499]: cannot borrow
as mutable more than once at a time.
错误的根源对我来说很清楚,但我想知道 - 解决它的正确方法是什么?
请注意,和 traits 使用一个公共字段来减少剩余的,所以对我来说,实现这两个字段是有意义的。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();
}
答:
您有以下几种选择:
使用泛型(静态调度)并采用实现 和 的单个参数。
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
采用共享引用并使用内部可变性。有两种方法可以做到这一点。第一种是将特征的方法更改为共享,并将内部可变性(例如
RefCell
)放在结构中。第二种是保持原样,但对游泳者来说也是如此。每种方法都有优点和缺点。&self
&RefCell<dyn Walker>
如果您既不能更改方法,也不能更改特征(例如,因为它们来自外部库),则可以保持原样,将 放入结构中,实现对 struct () 的共享引用的特征,并将可变引用传递给共享引用:。
RefCell
impl Walker for &'_ Dog
&mut &dog
评论