Reputation: 106
I have the following problem: I have two structs A and B, A implements a method, which takes another method as argument. I then create an instance of A within B. I then call the method of A with a method of B as argument. However, the passed method of B contains self as argument, since I have to access some other fields of B within the passed method.
My current plan would be to pass the method itself and self referencing an instance of B to the method in A. However, I struggle with the type definitions. I can hardcode the type of the second parameter to be B, however, I would like to keep it open, in case a struct C would also like to use the method in A. I therefore would like to set the type of the second parameter to the type of the struct, where the method in the first parameter is originating. Is there a way for achieving this? I was thinking about generics, however, I was not yet able to implement it.
Edit: added a minimal example in rust playground. What bothers me about this code, is, that I want to get rid of the hard coded &B in some_func_a, since this does not work with struct C.
struct A {}
impl A {
fn some_func_a(&self, passed_fn: fn(&B, i32), cur_self: &B) {
passed_fn(cur_self, 21);
}
}
struct B {
some_val: i32,
}
struct C {
some_val: u32, // different field here than in B
}
impl B {
fn call_fn_in_a(&self, a: A) {
a.some_func_a(B::some_func_b, self);
}
fn some_func_b(&self, passed_val: i32) {
println!("The value is {}, passed was {}", self.some_val, passed_val)
}
}
impl C {
fn call_fn_in_a(&self, a: A) {
// this line here breaks, since &B is hard coded
a.some_func_a(C::some_func_c, self);
}
fn some_func_c(&self, passed_val: i32) {
println!("this is a new function, val is {}, passed was {}", self.some_val, passed_val)
}
}
fn main() {
let a = A {};
let b = B {
some_val: 42,
};
let b = B {
some_val: 42,
};
b.call_fn_in_a(a);
}
Upvotes: 4
Views: 2697
Reputation: 168998
It sounds like you want a closure instead of a function pointer. This would allow you to eliminate the extra parameter of A::some_func_a()
altogether:
impl A {
fn some_func_a(&self, passed_fn: impl FnOnce(i32) -> ()) {
passed_fn(21);
}
}
Now we can call this function with any closure matching the call signature. For example:
impl B {
fn call_fn_in_a(&self, a: A) {
a.some_func_a(|v| self.some_func_b(v));
}
fn some_func_b(&self, passed_val: i32) {
println!("The value is {}, passed was {}", self.some_val, passed_val)
}
}
And likewise for C
.
Now there are no restrictions on what extra data the function needs. Before there wasn't even a way to express the situation where the callback didn't need the reference parameter. Now it doesn't matter because the closure can capture what it needs, and it's no longer A::some_func_a()
's responsibility.
Side note: you probably want your call_fn
methods to take the A
by reference (&A
), not by value. Otherwise calling these functions consumes the A
value, requiring you to make another one to call the functions again.
Upvotes: 2