Returning method from struct method

Hey there,
So I have a struct that implements a number of methods and I want one method that chooses which method to send back. I'm more familiar with python but don't know how to do this in rust yet. I want to be able to get the function but not call it immediately. I'm new to rust and would appreciate any help, feedback or directions on where to find out more on how to do this. Thanks!

Basic example:

struct Parser {
}

impl Parser {
    fn func_for_a(&mut self, param: String) {
        println!("In A: {}", param);
    }
    fn func_for_b(&mut self, param: String) {
        println!("In B {}", param);
    }
    fn choose(&mut self, sort: SomeEnum) -> <FUNCTION?> {
        match sort {
            SomeEnum::A => self.func_for_a,
            SomeEnum::B => self.func_for_b,
        }
    }
    fn main(&mut self, sort: SomeEnum) {
        let some_fun = choose(sort);
        // Have function but can do things before calling it!
        self.change_some_variable_to_five = 5;
        // Call function afterwards
        some_fun();
    }
}
1 Like

There is no automatic currying in Rust. If you want to return a function that's bound to self, you'll have to explicitly construct a closure.

However, that won't work here, given that you also want to mutate something else in the struct, which also requires a mutable borrow (and mutable borrows can't co-exist). You'll have to explicitly pass self instead: Playground.

(If you didn't need the intermediate mutation step, you could return a curried closure, like this.)

3 Likes

There's multiple ways to represent “functions” in Rust. When in need for any single type that can represent multiple different functions, we can narrow the choice down to function pointers and Fn/FnMut/FnOnce trait objects.

Your code example seems like you are asking for a way to have the returned some_fun be callable in and by itself, which means that some_fun must be some sort of value that also holds some sort of pointer to the original self (as self is available within the func_for_a/func_for_b functions, yet not passed in manually to the some_fun()) call. If a function wants to keep around any additional information, such as a pointer, it needs to be a closure, and the type to use is a trait object for one of the Fn traits. In this case let’s go with FnMut, because the self pointer is a mutable reference:

struct Parser {
    change_some_variable_to_five: i32,
}

enum SomeEnum {
    A, B
}

impl Parser {
    fn func_for_a(&mut self, param: String) {
        println!("In A: {}", param);
    }
    fn func_for_b(&mut self, param: String) {
        println!("In B: {}", param);
    }
    fn choose(&mut self, sort: SomeEnum) -> Box<dyn FnMut(String) + '_> {
        match sort {
            SomeEnum::A => Box::new(|p| self.func_for_a(p)),
            SomeEnum::B => Box::new(|p| self.func_for_b(p)),
        }
    }
    fn main(&mut self, sort: SomeEnum) {
        let mut some_fun = self.choose(sort);
        // Have function but can do things before calling it!
        // self.change_some_variable_to_five = 5;
        // Call function afterwards
        some_fun(String::from("hello"));
    }
}

Now, with this example code, once you uncomment the self.change_some_variable_to_five = 5 line, you’ll run into the consequences of keeping the mutable reference (self) around for a long-ish time as part of the some_fun closure.

   Compiling playground v0.0.1 (/playground)
error[E0506]: cannot assign to `self.change_some_variable_to_five` because it is borrowed
  --> src/lib.rs:25:9
   |
23 |         let mut some_fun = self.choose(sort);
   |                            ----------------- `self.change_some_variable_to_five` is borrowed here
24 |         // Have function but can do things before calling it!
25 |         self.change_some_variable_to_five = 5;
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.change_some_variable_to_five` is assigned to here but it was already borrowed
26 |         // Call function afterwards
27 |         some_fun(String::from("hello"));
   |         -------- borrow later used here

For more information about this error, try `rustc --explain E0506`.
error: could not compile `playground` due to previous error

Approaches for avoiding such conflict and making the borrow checker include the (sometimes dreaded) shared mutability constructs on one hand, or – in possible – one likes to avoid the problem by keeping a more keen eye on your references and only handing them to the functions that need them, when they need them, and for a short amount of time. This amounts in making the self argument an argument of the function that choose returns, e.g. Box<dyn FnMut(&mut Self, String) + '_> or Box<dyn Fn(&mut Self, String) + '_>, but by this point, the function you return does not actually require any run-time information anymore, so a functiton pointer would work as well: Function pointer types in Rust are written like e.g. fn(&mut Parser, String), and can be created from functions via implicit conversion; the way to name the function func_for_a itself is via a path like Parser::func_for_a (or Self::func_for_a since Self is an alias to Parser).

struct Parser {
    change_some_variable_to_five: i32,
}

enum SomeEnum {
    A, B
}

impl Parser {
    fn func_for_a(&mut self, param: String) {
        println!("In A: {}", param);
    }
    fn func_for_b(&mut self, param: String) {
        println!("In B: {}", param);
    }
    fn choose(&mut self, sort: SomeEnum) -> fn(&mut Parser, String) {
        match sort {
            SomeEnum::A => Self::func_for_a,
            SomeEnum::B => Self::func_for_b,
        }
    }
    fn main(&mut self, sort: SomeEnum) {
        let mut some_fun = self.choose(sort);
        // Have function but can do things before calling it!
        self.change_some_variable_to_five = 5;
        // Call function afterwards
        some_fun(self, String::from("hello"));
    }
}

Often, it’s also possible to avoid the need for function pointers altogether. E.g. in this case, the fn(&mut Parser, String) might actually be not much more useful than the SomeEnum enum you started out with. (Well, it may depend on your use-case, and how extendable you want the type to be.) So perhaps your best approach might be to just create a dispatching method

    fn func_for_either(&mut self, sort: SomeEnum, param: String) {
        match sort {
            SomeEnum::A => self.func_for_a(param),
            SomeEnum::B => self.func_for_b(param),
        }
    }

and then call that directly

    fn main(&mut self, sort: SomeEnum) {
        let my_sort = sort;
        // Have function but can do things before calling it!
        self.change_some_variable_to_five = 5;
        // Call function afterwards
        self.func_for_either(my_sort, String::from("hello"));
    }
4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.