I have a trait method that needs to mutate internal data of a struct and I was hoping I could pass a setter method as a parameter to the trait method. I want to do this so that I can be flexible with what gets mutated by specifying the function passed as a parameter.
This code is a simplification of what I am trying to do, it has the same compiler issue.
Thanks
Code
struct MyStruct {
i: usize
}
impl MyStruct {
fn set_i(mut self, i: usize) -> Self {
self.i = i;
self
}
fn new() -> Self{
MyStruct{ i: 0 }
}
}
trait MyTrait {
fn do_some_setting(&mut self, setter_function: fn(&mut Self, usize) -> Self ) {
setter_function(&mut self, 7);
}
}
impl MyTrait for MyStruct {}
fn main() {
let mut s = MyStruct::new()
.set_i(3);
assert_eq!(s.i, 3);
s.do_some_setting(MyStruct::set_i);
assert_eq!(s.i, 7);
}
Problem
error[E0308]: mismatched types
--> src/main.rs:27:23
|
27 | s.do_some_setting(MyStruct::set_i);
| ^^^^^^^^^^^^^^^ expected `&mut MyStruct`, found struct `MyStruct`
|
= note: expected fn pointer `for<'r> fn(&'r mut MyStruct, _) -> MyStruct`
found fn item `fn(MyStruct, _) -> MyStruct {MyStruct::set_i}`
This does not work because MyTrait::do_some_setting
expects a function whose first parameter is of type &mut Self
whereas the first parameter of MyStruct::set_i
is of type mut self
.
You can fix this by changing the signature of MyStruct::set_i
to set_i(&mut self, i: usize)
but then the compiler would complain about mismatched types
as it expects MyStruct::set_i
to return Self
but you'd be returning &mut Self
. You can either derive Clone
and return the struct after cloning it, or you can change the return type in the signature to &mut Self
.
The compiler will once again complain about mismatched types as setter_function
in MyTrait::do_some_setting
is a function that returns Self
, not &mut Self
. You'll have to change the signature of setter_function
to return &mut Self
.
The compiler will now complain that temporary value dropped while borrowed
at let mut s = MyStruct::new().set_i(3)
. You'll have to create MyStruct
first and then use set_i
on it.
At last, you'll end up with code that looks as follows:
struct MyStruct {
i: usize,
}
impl MyStruct {
fn set_i(&mut self, i: usize) -> &mut Self {
self.i = i;
self
}
fn new() -> Self {
MyStruct { i: 0 }
}
}
trait MyTrait {
fn do_some_setting(&mut self, setter_function: fn(&mut Self, usize) -> &mut Self) {
setter_function(self, 7);
}
}
impl MyTrait for MyStruct {}
fn main() {
let mut s = MyStruct::new();
s.set_i(3);
assert_eq!(s.i, 3);
s.do_some_setting(MyStruct::set_i);
assert_eq!(s.i, 7);
}