Processing CLI options from a hashmap of string callback function


#1

I am a total novice, and was looking to improve the ethereum sputnik-vm’s option processing.

There is a bit of redundant code in there, so I thought change this into a hashmap with a string key for the option name and a function to call which returns the right object (sputnik vm). If the string option however is not in the list of hashmap keys, it would be nice to print both the option and a list of valid option names.

So here is a little test program to try to get me started…

use std::collections::HashMap;
type CreateBox = fn() -> i32;
fn frontier_fn() -> i32 {
    1
}
fn homestead_fn() -> i32 {
    2
}

fn main() {
    let patch_opts: HashMap<&str, CreateBox> =
        [("frontier", frontier_fn), ("homestead", homestead_fn)]
            .iter()
            .cloned()
            .collect();
    let opt = "frontier";
    match patch_opts.get(&opt) {
        Some(patch_fn) => println!("Result: {}", patch_fn()),
        None => println!("Option {} not found", opt),
    }
}

When I compile this I get:


/home/vagrant/.cargo/bin/cargo run --manifest-path /home/vagrant/rust/hashmap/Cargo.toml 
   Compiling hashmap v0.1.0 (file:///home/vagrant/rust/hashmap)
error[E0308]: mismatched types
  --> src/main.rs:20:23
   |
20 |         ("homestead", homestead_fn),
   |                       ^^^^^^^^^^^^ expected fn item, found a different fn item
   |
   = note: expected type `fn() -> i32 {frontier_fn}`
              found type `fn() -> i32 {homestead_fn}`

error[E0308]: mismatched types
  --> src/main.rs:21:20
   |
21 |         ("eip150", eip150_fn),
   |                    ^^^^^^^^^ expected fn item, found a different fn item
   |
   = note: expected type `fn() -> i32 {frontier_fn}`
              found type `fn() -> i32 {eip150_fn}`

error[E0277]: the trait bound `std::collections::HashMap<&str, fn() -> i32>: std::iter::FromIterator<(&str, fn() -> i32 {frontier_fn})>` is not satisfied
  --> src/main.rs:24:10
   |
24 |         .collect();
   |          ^^^^^^^ a collection of type `std::collections::HashMap<&str, fn() -> i32>` cannot be built from an iterator over elements of type `(&str, fn() -> i32 {frontier_fn})`
   |
   = help: the trait `std::iter::FromIterator<(&str, fn() -> i32 {frontier_fn})>` is not implemented for `std::collections::HashMap<&str, fn() -> i32>`

error: aborting due to 3 previous errors

The first two errors seems to tell me that he function signatures don’t match, but I am at a loss to see why. The third and last message about iterators I don’t understand at all.

And as far as listing the keys, in the hashmap, I’ve just left that aside as isn’t as important as solving the first two problems.

(Also if you look at the sputnik code, the hashmap would be more complicated as there is an additional dimension needed in hash lookup.


#2

In Rust, each function has its own type, eg. fn() -> i32 {frontiner_fn} or fn() -> i32 {homestead_fn}. There also exists a function pointer type, in your example it’s fn() -> i32. What’s needed for your code to compile is to make a slight hint that you actually want an array of function pointers (as you see, Rust tries to assume that all the elements in array have the same type as the first one). The little as CreateBox is enough to provide this hint. Playground:

[("frontier", frontier_fn as CreateBox), ("homestead", homestead_fn)]

#3

Thanks! That worked - casting to the rescue which seems odd for a language like Rust.

I don’t think I could have guessed that solution or found it in the existing documentation, and I guess the last message with the iterator also had to do with Rust using a particular function signature rather than a function pointer type?


#4

This is technically a coercion, not a cast. But yeah, this lack of automatic coercion in places like this is a slight papercut until you learn about it :slight_smile:.