I have a Rust program with two versions of structs and their method implementations. The first version uses a function pointer, while the second uses a boxed trait object. Here is the code:
pub struct FnWrapper1 {
pub f: fn(&mut BoolAndFns1),
}
pub struct BoolAndFns1 {
pub b: bool,
pub fs: Vec<FnWrapper1>,
}
impl BoolAndFns1 {
pub fn meth(&mut self) {
if let Some(wrapper) = self.fs.last_mut() {
(wrapper.f)(self);
}
}
}
pub struct FnWrapper2 {
pub f: Box<dyn Fn(&mut BoolAndFns2)>,
}
pub struct BoolAndFns2 {
pub b: bool,
pub fs: Vec<FnWrapper2>,
}
impl BoolAndFns2 {
pub fn meth(&mut self) {
if let Some(wrapper) = self.fs.last_mut() {
(wrapper.f)(self);
}
}
}
fn main() {
let mut bf1 = BoolAndFns1 {
b: false,
fs: vec![FnWrapper1 {
f: |bf| {
bf.b = true;
},
}],
};
bf1.meth();
let mut bf2 = BoolAndFns2 {
b: false,
fs: vec![FnWrapper2 {
f: Box::new(|bf2| {
bf2.b = true;
}),
}],
};
bf2.meth();
}
When I try to compile this program, the Rust compiler gives an error for FnWrapper2
:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:30:16
|
29 | if let Some(wrapper) = self.fs.last_mut() {
| ------- first mutable borrow occurs here
30 | (wrapper.f)(self);
| ----------- ^^^^ second mutable borrow occurs here
| |
| first borrow later used by call
However, there is no error for FnWrapper1
.
As such, I have two questions:
The difference is, that function pointers do implement Copy
so wrapper.f
isn't borrowing from self
. You can avoid aliasing by temporarily removing the function from the vector instead of referencing it:
impl BoolAndFns2 {
pub fn meth(&mut self) {
if let Some(wrapper) = self.fs.pop() {
(wrapper.f)(self);
self.fs.push(wrapper);
}
}
}
If you don't want to or can't remove the pointer, you'll have to find a way to share the trait objects, without borrowing, for example by sharing ownership with Rc
:
use std::rc::Rc;
pub struct FnWrapper3 {
pub f: Rc<dyn Fn(&mut BoolAndFns3)>,
}
pub struct BoolAndFns3 {
pub b: bool,
pub fs: Vec<FnWrapper3>,
}
impl BoolAndFns3 {
pub fn meth(&mut self) {
if let Some(wrapper) = self.fs.last() {
let f = Rc::clone(&wrapper.f);
(f)(self);
}
}
}