How to create a function pointer according to another function pointer?

I'm writing atoi with state machine and it works perfectly.

struct Processor(Option<fn(processor: &mut Processor, c: char, a: &mut i32)>);

fn accu_add(c: char, a: &mut i32) {
    *a = a.saturating_mul(10).saturating_add(c.to_digit(10).unwrap() as i32)
}

fn accu_sub(c: char, a: &mut i32) {
    *a = a.saturating_mul(10).saturating_sub(c.to_digit(10).unwrap() as i32)
}

fn process_sign(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        c if c.is_whitespace() => (),
        '+' => *processor = Processor(Some(process_positive_digit)),
        '-' => *processor = Processor(Some(process_negative_digit)),
        '0' ..= '9' => { *processor = Processor(Some(process_positive_digit)); accu_add(c, a) },
        _ => *processor = Processor(None),
    }
}

fn process_positive_digit(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        '0' ..= '9' => accu_add(c, a),
        _ => *processor = Processor(None),
    }
}

fn process_negative_digit(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        '0' ..= '9' => accu_sub(c, a),
        _ => *processor = Processor(None),
    }
}

impl Solution {
    pub fn my_atoi(s: String) -> i32 {
        let (mut processor, mut a) = (Processor(Some(process_sign)), 0);
        for c in s.chars() {
            match processor.0 {
                Some(f) => f(&mut processor, c, &mut a),
                None => return a,
            }
        }
        return a;
    }
}

But there are two functions which are too similar.

fn process_positive_digit(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        '0' ..= '9' => accu_add(c, a),
        _ => *processor = Processor(None),
    }
}
fn process_negative_digit(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        '0' ..= '9' => accu_sub(c, a),
        _ => *processor = Processor(None),
    }
}

So I want to generate the two functions with a super function (takes a function pointer and returns a function pointer), as bind does in js/ts.

Here is the code:

struct Processor(Option<fn(processor: &mut Processor, c: char, a: &mut i32)>);

fn accu_add(c: char, a: &mut i32) {
    *a = a.saturating_mul(10).saturating_add(c.to_digit(10).unwrap() as i32)
}

fn accu_sub(c: char, a: &mut i32) {
    *a = a.saturating_mul(10).saturating_sub(c.to_digit(10).unwrap() as i32)
}

fn process_sign(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        c if c.is_whitespace() => (),
        '+' => *processor = Processor(Some(process_digit(accu_add))),
        '-' => *processor = Processor(Some(process_negative_digit)),
        '0' ..= '9' => { *processor = Processor(Some(process_positive_digit)); accu_add(c, a) },
        _ => *processor = Processor(None),
    }
}

fn process_digit(f: &'static fn(char, &mut i32)) -> impl Fn(&mut Processor, char, &mut i32) {
    |processor: &mut Processor, c: char, a: &mut i32| match c {
        '0' ..= '9' => f(c, a),
        _ => *processor = Processor(None),
    }
}

fn process_positive_digit(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        '0' ..= '9' => accu_add(c, a),
        _ => *processor = Processor(None),
    }
}

fn process_negative_digit(processor: &mut Processor, c: char, a: &mut i32) {
    match c {
        '0' ..= '9' => accu_sub(c, a),
        _ => *processor = Processor(None),
    }
}

impl Solution {
    pub fn my_atoi(s: String) -> i32 {
        let (mut processor, mut a) = (Processor(Some(process_sign)), 0);
        for c in s.chars() {
            match processor.0 {
                Some(f) => f(&mut processor, c, &mut a),
                None => return a,
            }
        }
        return a;
    }
}

Notice the super function here. I choose closure.

fn process_digit(f: &'static fn(char, &mut i32)) -> impl Fn(&mut Processor, char, &mut i32) {
    |processor: &mut Processor, c: char, a: &mut i32| match c {
        '0' ..= '9' => f(c, a),
        _ => *processor = Processor(None),
    }
}

Then the compiler refuses it defiantly.

Line 14: Char 58: error: mismatched types (solution.rs)
   |
14 |         '+' => *processor = Processor(Some(process_digit(accu_add))),
   |                                            ------------- ^^^^^^^^ expected `&fn(char, &mut i32)`, found fn item
   |                                            |
   |                                            arguments to this function are incorrect
   |
   = note: expected reference `&'static for<'a> fn(_, &'a mut _)`
                found fn item `for<'a> fn(_, &'a mut _) {accu_add}`
Line 21: Char 4: note: function defined here (solution.rs)
   |
21 | fn process_digit(f: &'static fn(char, &mut i32)) -> impl Fn(&mut Processor, char, &mut i32) {
   |    ^^^^^^^^^^^^^ ------------------------------

Line 14: Char 44: error: mismatched types (solution.rs)
   |
14 |         '+' => *processor = Processor(Some(process_digit(accu_add))),
   |                                       ---- ^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found opaque type
   |                                       |
   |                                       arguments to this enum variant are incorrect
...
21 | fn process_digit(f: &'static fn(char, &mut i32)) -> impl Fn(&mut Processor, char, &mut i32) {
   |                                                     --------------------------------------- the found opaque type
   |
   = note: expected fn pointer `for<'a, 'b> fn(&'a mut Processor, char, &'b mut i32)`
             found opaque type `impl for<'a, 'b> Fn(&'a mut Processor, char, &'b mut i32)`
Line 14: Char 39: help: the type constructed contains `impl for<'a, 'b> Fn(&'a mut Processor, char, &'b mut i32)` due to the type of the argument passed (solution.rs)
   |
14 |         '+' => *processor = Processor(Some(process_digit(accu_add))),
   |                                       ^^^^^-----------------------^
   |                                            |
   |                                            this argument influences the type of `Some`
Line 596: Char 5: note: tuple variant defined here (solution.rs)

For more information about this error, try `rustc --explain E0308`.
error: could not compile `prog` (bin "prog") due to 2 previous errors

Question: Is it possible to generate a function pointer from another function pointer?

No, it is not possible to do this directly. That is because function pointers have to all exist at compile time, pointing to code without data, and your -> impl Fn(...) is code with data (the second function pointer).

Your options in this type of situation are:

  1. Define the add/subtract behavior using generics, such as two types implementing a trait, so that you have process_digit::<Add> and process_digit::<Sub> instead of process_digit(accu_add) and process_digit(accu_sub). These generic parameters are resolved at compile time into two distinct function pointers.

  2. Deduplicate the body of the function only:

    fn process_digit(&mut Processor, c: char, a: &mut i32, f: impl Fn(char, &mut i32)) {
        match c {
            '0' ..= '9' => f(c, a),
            _ => *processor = Processor(None),
        }
    }
    fn process_positive_digit(processor: &mut Processor, c: char, a: &mut i32) {
        process_digit(processor, c, a, accu_add)
    }
    fn process_negative_digit(processor: &mut Processor, c: char, a: &mut i32) {
        process_digit(processor, c, a, accu_sub)
    }
    
  3. An messy and unsafe trick involving applying option 1 to function item types, which I would not recommend for routine use.

Out of these choices, I would generally recommend option 2 as the clean and straightforward one.

Unrelated to your question: there is no reason to write &'static fn(char, &mut i32) instead of just fn(char, &mut i32), because a fn() type is already a copiable pointer, so &'static doesn’t have any benefit, only additional constraint.[1]


  1. One could argue this is a design mistake in Rust, and instead fn() should be a type for the code, so that &fn() is the type of a pointer to the code and can have a lifetime shorter than 'static if you want, but in the Rust we have, fn() is a pointer type. ↩︎

1 Like

Option 2 works well.

But these two functions are still similar.

fn accu_add(c: char, a: &mut i32) {
    *a = a.saturating_mul(10).saturating_add(c.to_digit(10).unwrap() as i32)
}

fn accu_sub(c: char, a: &mut i32) {
    *a = a.saturating_mul(10).saturating_sub(c.to_digit(10).unwrap() as i32)
}

Is it possible to use the similar way of option 2 but pass a pointer to method in it?

fn accu_add(c: char, a: &mut i32, method: /* method pointer */) {
  *a = a.saturating_mul(10).method(c.to_digit(10).unwrap() as i32)
}

all methods are just functions that can be used in method form. there is no such thing as a method pointers, because methods are just functions. i32::saturating_add is simply a function (i32, i32) -> i32

so

fn accu(c: char, a: &mut i32, method: impl Fn(i32, i32) -> i32) {
  *a = method(a.saturating_mul(10), c.to_digit(10).unwrap() as i32)
}

would do it

2 Likes

Fascinating!

Here I use function pointer and it works well. Can I use Processor(|processor: &mut Processor, c: char, a: &mut i32| {...}) in this program? If I can, how to define the struct Processor?

as kpreid said :

it is only because of this tha different function pointers have the same type, and can be stored in the same struct field.
as such, you can only convert a closure to a function pointer if it doesn't capture any context. if that is the case, then said closure will basically just be an anonymous function, and will be automatically be converted to a function pointer.

Processor(|processor: &mut Processor, c: char, a: &mut i32| { *a += 1 })

is fine, but

let x = todo!();
Processor(|processor: &mut Processor, c: char, a: &mut i32| { *a += x })

is not (it captures x)

1 Like

Got it. Thanks. These information should be written into the text book.

1 Like

Thanks for the link of the book. I'm reading it.

But this line in the book:

let list_of_strings: Vec =
list_of_numbers.iter().map(ToString::to_string).collect();

Listing 20-30: Using the String::to_string function with the map method to convert numbers to strings

So, is .map(ToString::to_string) a typo? Or is String::to_string a typo?

As far as the language is concerned, those are the same function. The full name of the function being called is <String as ToString>::to_string (String is a type and ToString is a trait), and you can refer to it using either of the parts since it’s not ambiguous. The book appears to be inconsistent there, but changing it in either direction would be equally correct.

3 Likes

neither.
there is code somewhere like this

impl ToString for String {

   fn to_string(&self) -> String  { ... }
}

(not exactly because of blanket impl but kinda)
if you want to say the name of this method as a function, you can write String::to_string (using the name of the type), ToString::to_string (using the name of the trait), or the full name <String as ToString>::to_string (using both).
only using both can be completely unambiguous, as ToString::to_string could refer to an implementation on another type, and String::to_string could refer to an inherent method or a method from another trait.
but usually the compiler has enough info to figure it out with the simpler version.

1 Like

This information is completely new to me (an experienced programmer of other languages). Strongly suggest it should be linked into the code snippet I cited.

this is mentionned earlier in the same chapter
they can't really repeat every feature every time, so if you don't go through the book in order you will get confussed by things

Me again and I'm just stubborn.

This time I use closure finally.

struct Parser(Option<Box<dyn Fn(&mut Parser, char, &mut i32)>>);

fn parse_sign(parser: &mut Parser, c: char, a: &mut i32) {
    fn accu(c: char, a: &mut i32, f: fn(i32, i32) -> i32) {
        *a = f(a.saturating_mul(10), c.to_digit(10).unwrap() as i32)
    }

    fn process_digit(parser: &mut Parser, c: char, a: &mut i32, f: fn(i32, i32) -> i32) {
        match c {
            '0' ..= '9' => accu(c, a, f),
            _ => *parser = Parser(None),
        }
    }

    match c {
        c if c.is_whitespace() => (),
        '+' => *parser = Parser(Some(Box::new(|parser: &mut Parser, c: char, a: &mut i32| process_digit(parser, c, a, i32::saturating_add)))),
        '-' => *parser = Parser(Some(Box::new(|parser: &mut Parser, c: char, a: &mut i32| process_digit(parser, c, a, i32::saturating_sub)))),
        '0' ..= '9' => {
            *parser = Parser(Some(Box::new(|parser: &mut Parser, c: char, a: &mut i32| process_digit(parser, c, a, i32::saturating_add))));
            accu(c, a, i32::saturating_add)
        },
        _ => *parser = Parser(None),
    }
}

impl Solution {
    pub fn my_atoi(s: String) -> i32 {
        let (mut parser, mut a) = (Parser(Some(Box::new(parse_sign))), 0);
        for c in s.chars() {
            match parser.0 {
                Some(ref f) => f(&mut parser, c, &mut a),
                None => return a,
            }
        }
        return a;
    }
}

But there is a tiny little error:

Line 32: Char 34: error: cannot borrow `parser` as mutable because it is also borrowed as immutable (solution.rs)
   |
32 |                 Some(ref f) => f(&mut parser, c, &mut a),
   |                      -----     - ^^^^^^^^^^^ mutable borrow occurs here
   |                      |         |
   |                      |         immutable borrow later used by call
   |                      immutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `prog` (bin "prog") due to 1 previous error

Is it possible to get it done?

Try match parser.0.take().

Your code, as written, is trying to overwrite parser.0 before it’s done executing the previous invocation of the parser function. .take() replaces parser.0 with None and gives you the previous parser, so then calling that parser with a &mut parser argument doesn’t require overlapping borrows of parser.

It compiles successfully. But runs incorrectly. Because the original value of parser.0 really matters.

Should I use RC instead of Box?
Or should I clone something?

well first i would strongly recommend against doing rust on leetcode.

their horrible exercises are easily recognizable with the stupide impl Solution, and they always use the wrongest type possible.
if you were to ever write an atoi, it should be my_atoi(&str) -> i32, not String.
to see an example, you can look at str::parse, which is the correct function for doing that in std.
i would personally recommend exercism, but there may be others that are just as good.

secondly, the answer is not with closures. you have come back to one of the original issue you had in your previous post ; you can't have a reference to the function inside the Parser while modifyng it at the same time.
that is why i said last time

but closures are not all copy. why have you decided to use closures exactly ? there is no need, you do not capture any context.
simply using function pointers again fixes it, and removes the unnecessary and wasteful boxing.

struct Parser(Option<fn(&mut Parser, char, &mut i32)>);

fn parse_sign(parser: &mut Parser, c: char, a: &mut i32) {
    fn accu(c: char, a: &mut i32, f: fn(i32, i32) -> i32) {
        *a = f(a.saturating_mul(10), c.to_digit(10).unwrap() as i32)
    }

    fn process_digit(parser: &mut Parser, c: char, a: &mut i32, f: fn(i32, i32) -> i32) {
        match c {
            '0' ..= '9' => accu(c, a, f),
            _ => *parser = Parser(None),
        }
    }

    match c {
        c if c.is_whitespace() => (),
        '+' => *parser = Parser(Some(|parser: &mut Parser, c: char, a: &mut i32| process_digit(parser, c, a, i32::saturating_add))),
        '-' => *parser = Parser(Some(|parser: &mut Parser, c: char, a: &mut i32| process_digit(parser, c, a, i32::saturating_sub))),
        '0' ..= '9' => {
            *parser = Parser(Some(|parser: &mut Parser, c: char, a: &mut i32| process_digit(parser, c, a, i32::saturating_add)));
            accu(c, a, i32::saturating_add)
        },
        _ => *parser = Parser(None),
    }
}

struct Solution;

impl Solution {
    pub fn my_atoi(s: String) -> i32 {
        let (mut parser, mut a) = (Parser(Some(parse_sign)), 0);
        for c in s.chars() {
            match parser.0 {
                Some(f) => f(&mut parser, c, &mut a),
                None => return a,
            }
        }
        return a;
    }
}

Switch back to function pointer and it works as wish.

But leetcode is innocent. I'm looking for an appropriate programming language in the algorithm part (GPU intense) of our AI production. I'd like to experience the most horrible part of Rust in order to preclude the risk of production development before it's too late. The stupid leetcode puzzles just meet the requirement.

but closures are not all copy. why have you decided to use closures exactly ? there is no need, you do not capture any context.

I'm not interested in atoi. I just need to know what Rust can do or can't do. If I have to use closure here, it is impossible to write f(&mut parser). Am I right?

Thank you all for answering my questions again and again. I wish you good and wish Rust good. I master almost 10 programming languages. And I don't know if Rust is my last. I just hope I could help this beautiful language in some way.

As a beginner, one of the most valuable things you can do is notice when the compiler is giving you unhelpful advice, and file issues about that.

3 Likes