As pointed out by @SilvioMayolo, value "identity" is not a thing in Rust because Rust's values are not heap-allocated by default. While you can take an address of any value, you can't use it as to represent identity the address changes every time the value is moved to a different variable, passed to a function, or inserted in a container. You can make the address stable by heap-allocating the value, but that requires an allocation when the value is created, and an extra dereference on every access.
For values that heap-allocate their content, such as String
s, you could use the address of the contents to represent identity, as shown in @Smitop's answer. But that is also not a good representation of identity because it changes any time the string re-allocates, e.g. if you append some data to it. If you never plan to grow your strings, then that option will work well. Otherwise, you must use something else.
In general, instead of using an address to represent identity, you can explicitly track the identity as part of the object. Nothing stops you from adding a field representing identity, and assigning it in the constructor:
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
pub struct Person {
id: u64,
name: String,
}
impl Person {
/// Create a new unique person.
pub fn new(name: String) -> Self {
Person {
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
name,
}
}
/// Identity of a `Person`.
///
/// Two persons with the same name will still have different identities.
pub fn id(&self) -> u64 {
self.id
}
}
Then you can implement Hash
and Eq
to use this identity:
impl Hash for Person {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Eq for Person {}
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
// We can't #[derive(Clone)] because that would clone the id, and we
// want to generate a new one instead.
impl Clone for Person {
fn clone(&self) -> Person {
Person::new(self.name.clone())
}
}
Playground