In Rust, I want to treat enums as equal, but still be able to distinguish different instances by pointer. Here's a toy example:
use self::Piece::*;
use std::collections::HashMap;
#[derive(Eq, PartialEq)]
enum Piece {
Rook,
Knight,
}
fn main() {
let mut positions: HashMap<&Piece, (u8, u8)> = HashMap::new();
let left_rook = Rook;
let right_rook = Rook;
positions.insert(&left_rook, (0, 0));
positions.insert(&right_rook, (0, 7));
}
However, the compiler wants me to define Hash
on Piece
:
error[E0277]: the trait bound `Piece: std::hash::Hash` is not satisfied
--> src/main.rs:11:52
|
11 | let mut positions: HashMap<&Piece, (u8, u8)> = HashMap::new();
| ^^^^^^^^^^^^ the trait `std::hash::Hash` is not implemented for `Piece`
|
= note: required because of the requirements on the impl of `std::hash::Hash` for `&Piece`
= note: required by `<std::collections::HashMap<K, V>>::new`
error[E0599]: no method named `insert` found for type `std::collections::HashMap<&Piece, (u8, u8)>` in the current scope
--> src/main.rs:15:15
|
15 | positions.insert(&left_rook, (0, 0));
| ^^^^^^
|
= note: the method `insert` exists but the following trait bounds were not satisfied:
`&Piece : std::hash::Hash`
error[E0599]: no method named `insert` found for type `std::collections::HashMap<&Piece, (u8, u8)>` in the current scope
--> src/main.rs:16:15
|
16 | positions.insert(&right_rook, (0, 7));
| ^^^^^^
|
= note: the method `insert` exists but the following trait bounds were not satisfied:
`&Piece : std::hash::Hash`
I want equality defined on my enums so that one Rook
is equal to another. However, I want to be able to distinguish different Rook
instances in my positions
hashmap.
How do I do this? I don't want to define Hash
on Piece
, but surely hashing is already defined on pointers?
There's a difference between raw pointers (
*const T
,*mut T
) and references (&T
,&mut T
) in Rust. You have a reference.Hash
is defined for references as delegating to the hash of the referred-to item:However, it is defined for raw pointers as you desire:
And that works:
However, using either references or raw pointers here is iffy at best.
If you use a reference, the compiler will stop you from using the hashmap once you have moved the values you have inserted as the reference will no longer be valid.
If you use raw pointers, the compiler won't stop you, but then you will have dangling pointers which can lead to memory unsafety.
In your case, I think I'd try to restructure the code so that a piece is unique beyond the memory address. Perhaps just some incremented number:
If that seems impossible, you can always
Box
the piece to give it a stable memory address. This latter solution is more akin to languages like Java where everything is heap-allocated by default.As Francis Gagné says:
You can create a struct that handles reference equality:
And then use that: