// SPDX-License-Identifier: MPL-2.0 //! Same as the standard `Arc`, except that it can be used as the key type of a hash table. //! //! # Motivation //! //! A type `K` is _keyable_ if it can be used as the key type for a hash map. Specifically, //! according to the document of `std::collections::HashMap`, the type `K` must satisfy //! the following properties. //! //! 1. It implements the `Eq` and `Hash` traits. //! 2. The two values of `k1` and `k2` of type `K` equal to each other, //! if and only if their hash values equal to each other. //! 3. The hashes of a value of `k` of type `K` cannot change while it //! is in a map. //! //! Sometimes we want to use `Arc` as the key type for a hash map but cannot do so //! since `T` does not satisfy the properties above. For example, a lot of types //! do not or cannot implemennt the `Eq` trait. This is when `KeyableArc` can come //! to your aid. //! //! # Overview //! //! For any type `T`, `KeyableArc` satisfies all the properties to be keyable. //! This can be achieved easily and efficiently as we can simply use the address //! of the data (of `T` type) of a `KeyableArc` object in the heap to determine the //! equality and hash of the `KeyableArc` object. As the address won't change for //! an immutable `KeyableArc` object, the hash and equality also stay the same. //! //! This crate is `#[no_std]` compatible, but requires the `alloc` crate. //! //! # Usage //! //! Here is a basic example to how that `KeyableArc` is keyable even when `T` //! is not. //! //! ```rust //! use std::collections::HashMap; //! use std::sync::Arc; //! use keyable_arc::KeyableArc; //! //! struct Dummy; // Does not implement Eq and Hash //! //! let map: HashMap, String> = HashMap::new(); //! ``` //! //! `KeyableArc` is a reference counter-based smart pointer, just like `Arc`. //! So you can use `KeyableArc` the same way you would use `Arc`. //! //! ```rust //! use std::sync::atomic::{AtomicU64, Ordering::Relaxed}; //! use keyable_arc::KeyableArc; //! //! let key_arc0 = KeyableArc::new(AtomicU64::new(0)); //! let key_arc1 = key_arc0.clone(); //! assert!(key_arc0.load(Relaxed) == 0 && key_arc1.load(Relaxed) == 0); //! //! key_arc0.fetch_add(1, Relaxed); //! assert!(key_arc0.load(Relaxed) == 1 && key_arc1.load(Relaxed) == 1); //! ``` //! //! # Differences from `Arc` //! //! Notice how `KeyableArc` differs from standard smart pointers in determining equality? //! Two `KeyableArc` objects are considered different even when their data have the same //! value. //! //! ```rust //! use keyable_arc::KeyableArc; //! //! let key_arc0 = KeyableArc::new(0); //! let key_arc1 = key_arc0.clone(); //! assert!(key_arc0 == key_arc1); //! assert!(*key_arc0 == *key_arc1); //! //! let key_arc1 = KeyableArc::new(0); //! assert!(key_arc0 != key_arc1); //! assert!(*key_arc0 == *key_arc1); //! ``` //! //! `KeyableArc` is simply a wrapper of `Arc. So converting between them //! through the `From` and `Into` traits is zero cost. //! //! ```rust //! use std::sync::Arc; //! use keyable_arc::KeyableArc; //! //! let key_arc: KeyableArc = Arc::new(0).into(); //! let arc: Arc = KeyableArc::new(0).into(); //! ``` //! //! # The weak version //! //! `KeyableWeak` is the weak version of `KeyableArc`, just like `Weak` is //! that of `Arc`. And of course, `KeyableWeak` is also _keyable_ for any //! type `T`. // TODO: Add `KeyableBox` or other keyable versions of smart pointers. // If this is needed in the future, this crate should be renamed to `keyable`. // TODO: Add the missing methods offered by `Arc` or `Weak` but not their // keyable counterparts. #![cfg_attr(not(test), no_std)] #![feature(coerce_unsized)] #![feature(unsize)] #![deny(unsafe_code)] extern crate alloc; use alloc::sync::{Arc, Weak}; use core::{ borrow::Borrow, cmp::Ordering, fmt, hash::{Hash, Hasher}, marker::Unsize, ops::{CoerceUnsized, Deref}, }; /// Same as the standard `Arc`, except that it can be used as the key type of a hash table. #[repr(transparent)] pub struct KeyableArc(Arc); impl KeyableArc { /// Constructs a new instance of `KeyableArc`. #[inline] pub fn new(data: T) -> Self { Self(Arc::new(data)) } } impl KeyableArc { /// Returns a raw pointer to the object `T` pointed to by this `KeyableArc`. #[inline] pub fn as_ptr(this: &Self) -> *const T { Arc::as_ptr(&this.0) } /// Creates a new `KeyableWeak` pointer to this allocation. #[inline] pub fn downgrade(this: &Self) -> KeyableWeak { Arc::downgrade(&this.0).into() } /// Gets the number of strong pointers pointing to this allocation. #[inline] pub fn strong_count(this: &Self) -> usize { Arc::strong_count(&this.0) } /// Gets the number of weak pointers pointing to this allocation. #[inline] pub fn weak_count(this: &Self) -> usize { Arc::weak_count(&this.0) } } impl Deref for KeyableArc { type Target = T; #[inline] fn deref(&self) -> &T { &self.0 } } impl AsRef for KeyableArc { #[inline] fn as_ref(&self) -> &T { self } } impl Borrow for KeyableArc { #[inline] fn borrow(&self) -> &T { self } } impl From> for KeyableArc { #[inline] fn from(arc: Arc) -> Self { Self(arc) } } impl From> for Arc { #[inline] fn from(value: KeyableArc) -> Self { value.0 } } impl PartialEq for KeyableArc { fn eq(&self, other: &Self) -> bool { core::ptr::eq(Arc::as_ptr(&self.0), Arc::as_ptr(&other.0)) } } impl Eq for KeyableArc {} impl Ord for KeyableArc { fn cmp(&self, other: &Self) -> Ordering { Arc::as_ptr(&self.0) .cast::<()>() .cmp(&Arc::as_ptr(&other.0).cast::<()>()) } } impl PartialOrd for KeyableArc { fn partial_cmp(&self, other: &KeyableArc) -> Option { Some(self.cmp(other)) } } impl Hash for KeyableArc { fn hash(&self, s: &mut H) { Arc::as_ptr(&self.0).hash(s) } } impl Clone for KeyableArc { fn clone(&self) -> Self { Self(self.0.clone()) } } impl fmt::Debug for KeyableArc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } //========================================================= // The weak version //========================================================= /// The weak counterpart of `KeyableArc`, similar to `Weak`. /// /// `KeyableWeak` is also _keyable_ for any type `T` just like /// `KeyableArc`. #[repr(transparent)] pub struct KeyableWeak(Weak); impl KeyableWeak { /// Constructs a new `KeyableWeak`, without allocating any memory. /// Calling `upgrade` on the return value always gives `None`. #[inline] #[expect(clippy::new_without_default)] pub fn new() -> Self { Self(Weak::new()) } /// Returns a raw pointer to the object `T` pointed to by this `KeyableWeak`. /// /// The pointer is valid only if there are some strong references. /// The pointer may be dangling, unaligned or even null otherwise. #[inline] pub fn as_ptr(&self) -> *const T { self.0.as_ptr() } } impl KeyableWeak { /// Attempts to upgrade the Weak pointer to an Arc, /// delaying dropping of the inner value if successful. /// /// Returns None if the inner value has since been dropped. #[inline] pub fn upgrade(&self) -> Option> { self.0.upgrade().map(|arc| arc.into()) } /// Gets the number of strong pointers pointing to this allocation. #[inline] pub fn strong_count(&self) -> usize { self.0.strong_count() } /// Gets the number of weak pointers pointing to this allocation. #[inline] pub fn weak_count(&self) -> usize { self.0.weak_count() } } impl PartialEq for KeyableWeak { fn eq(&self, other: &Self) -> bool { core::ptr::eq(self.0.as_ptr(), other.0.as_ptr()) } } impl Eq for KeyableWeak {} impl Ord for KeyableWeak { fn cmp(&self, other: &Self) -> Ordering { self.0 .as_ptr() .cast::<()>() .cmp(&other.0.as_ptr().cast::<()>()) } } impl PartialOrd for KeyableWeak { fn partial_cmp(&self, other: &KeyableWeak) -> Option { Some(self.cmp(other)) } } impl Hash for KeyableWeak { fn hash(&self, s: &mut H) { self.0.as_ptr().hash(s) } } impl From> for KeyableWeak { #[inline] fn from(weak: Weak) -> Self { Self(weak) } } impl From> for Weak { fn from(value: KeyableWeak) -> Self { value.0 } } impl Clone for KeyableWeak { fn clone(&self) -> Self { Self(self.0.clone()) } } impl fmt::Debug for KeyableWeak { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "(KeyableWeak)") } } // Enabling type coercing, e.g., converting from `KeyableArc` to `KeyableArc`, // where `T` implements `S`. impl, U: ?Sized> CoerceUnsized> for KeyableArc {} #[cfg(test)] mod tests { use super::*; #[test] fn downgrade_and_upgrade() { let arc = KeyableArc::new(1); let weak = KeyableArc::downgrade(&arc); assert!(arc.clone() == weak.upgrade().unwrap()); assert!(weak == KeyableArc::downgrade(&arc)); } #[test] fn debug_format() { println!("{:?}", KeyableArc::new(1u32)); println!("{:?}", KeyableWeak::::new()); } #[test] fn use_as_key() { use std::collections::HashMap; let mut map: HashMap, u32> = HashMap::new(); let key = KeyableArc::new(1); let val = 1; map.insert(key.clone(), val); assert!(map.get(&key) == Some(&val)); assert!(map.remove(&key) == Some(val)); assert!(map.keys().count() == 0); } #[test] fn as_trait_object() { trait DummyTrait {} struct DummyStruct; impl DummyTrait for DummyStruct {} let arc_struct = KeyableArc::new(DummyStruct); let arc_dyn0: KeyableArc = arc_struct.clone(); let arc_dyn1: KeyableArc = arc_struct.clone(); assert!(arc_dyn0 == arc_dyn1); } }