Search code examples
rustreferencehashmap

Creating a HashMap with a function with reference paramaters as key


I'm trying to create a HashMap where the key type is a function with a reference paramater. let mut hm: HashMap<fn(&u32) -> u32, u32> = HashMap::new();. This works fine, but i cant insert anything into the map. The compiler says it's not legal since trait bounds weren't satisfied

rules.insert(
  |v: &u32| v + 1,
  0,
);

gives

the method `insert` exists but the following trait bounds were not satisfied:
           `for<'r> fn(&'r u32) -> u32: Eq`
           `for<'r> fn(&'r u32) -> u32: Hash`

I've read about Lifetimes in the various texts, but i can't figure out how to solve this.

Background: I'm implementing wave function collapse. I want my implementation to be generic in the sense that any "grid" can be used, 1d, 2d, 3d, nd, hexagonal etc. To do this i use a "ruleset" which is a hashmap where the key is a function that takes a cell coordinate and returns its neighbours, and the value is the rule for how to collapse said neighbours' states. The key function takes an index and a reference to the "grid" and returns the index of the neihbour.


Solution

  • If you want to support multiple grid implementations, the most natural approach is to define a Grid trait that abstracts differences between the grids. Here's one I made up, loosely based on your use case description:

    enum CollapseRule {
        A,
        B,
    }
    
    trait Grid {
        const COLLAPSE_RULE: CollapseRule;
    
        fn neighbours(&self, index: usize) -> Vec<usize>;
    }
    
    #[derive(Clone, Debug, Default)]
    struct Grid1d {
        vertices: Vec<f64>,
    }
    
    impl Grid for Grid1d {
        const COLLAPSE_RULE: CollapseRule = CollapseRule::A;
    
        fn neighbours(&self, index: usize) -> Vec<usize> {
            let len = self.vertices.len();
            match index {
                0 => vec![1],
                _ if index < len => vec![index - 1, index + 1],
                _ if index == len => vec![index - 1],
                _ => panic!(),
            }
        }
    }
    

    Whereever your code needs to accept a grid, you can accept a generic type G with a trait bound G: Grid, and any interaction with the grid happens via the trait. Your trait will likely need more functions than in my simplistic example.