Skip to main content

iddqd/id_ord_map/
trait_defs.rs

1//! Trait definitions for `IdOrdMap`.
2
3use alloc::{boxed::Box, rc::Rc, sync::Arc};
4use core::hash::Hash;
5
6/// An element stored in an [`IdOrdMap`].
7///
8/// This trait is used to define the key type for the map.
9///
10/// # Examples
11///
12/// ```
13/// use iddqd::{IdOrdItem, IdOrdMap, id_upcast};
14///
15/// // Define a struct with a key.
16/// #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
17/// struct MyItem {
18///     id: String,
19///     value: u32,
20/// }
21///
22/// // Implement IdOrdItem for the struct.
23/// impl IdOrdItem for MyItem {
24///     // Keys can borrow from the item.
25///     type Key<'a> = &'a str;
26///
27///     fn key(&self) -> Self::Key<'_> {
28///         &self.id
29///     }
30///
31///     id_upcast!();
32/// }
33///
34/// // Create an IdOrdMap and insert items.
35/// let mut map = IdOrdMap::new();
36/// map.insert_unique(MyItem { id: "foo".to_string(), value: 42 }).unwrap();
37/// map.insert_unique(MyItem { id: "bar".to_string(), value: 20 }).unwrap();
38/// ```
39///
40/// [`IdOrdMap`]: crate::IdOrdMap
41pub trait IdOrdItem {
42    /// The key type.
43    ///
44    /// The [`Ord`] implementation is used for ordered comparisons, while the
45    /// [`Hash`] implementation is used for [`RefMut`]'s change detection.
46    /// Ideally [`Hash`] would only be required on the methods that need it, but
47    /// we require it here due to limitations in current versions of Rust.
48    ///
49    /// As required by Rust, the [`Hash`] and [`Ord`] (and [`Eq`], which is
50    /// required by [`Ord`]) implementations must agree with each other. In
51    /// particular, if two keys A and B return
52    /// [`Ordering::Equal`](std::cmp::Ordering::Equal), then [`Hash::hash`] must
53    /// return the same value for both A and B. (Strictly speaking, the converse
54    /// is not required, but it's always best to try and match the two up to
55    /// hash collisions.)
56    ///
57    /// [`RefMut`]: crate::id_ord_map::RefMut
58    type Key<'a>: Ord + Hash
59    where
60        Self: 'a;
61
62    /// Retrieves the key.
63    fn key(&self) -> Self::Key<'_>;
64
65    /// Upcasts the key to a shorter lifetime, in effect asserting that the
66    /// lifetime `'a` on [`IdOrdItem::Key`] is covariant.
67    ///
68    /// Typically implemented via the [`id_upcast`] macro.
69    ///
70    /// [`id_upcast`]: crate::id_upcast
71    fn upcast_key<'short, 'long: 'short>(
72        long: Self::Key<'long>,
73    ) -> Self::Key<'short>;
74}
75
76macro_rules! impl_for_ref {
77    ($type:ty) => {
78        impl<'b, T: 'b + ?Sized + IdOrdItem> IdOrdItem for $type {
79            type Key<'a>
80                = T::Key<'a>
81            where
82                Self: 'a;
83
84            fn key(&self) -> Self::Key<'_> {
85                (**self).key()
86            }
87
88            fn upcast_key<'short, 'long: 'short>(
89                long: Self::Key<'long>,
90            ) -> Self::Key<'short>
91            where
92                Self: 'long,
93            {
94                T::upcast_key(long)
95            }
96        }
97    };
98}
99
100impl_for_ref!(&'b T);
101impl_for_ref!(&'b mut T);
102
103macro_rules! impl_for_box {
104    ($type:ty) => {
105        impl<T: ?Sized + IdOrdItem> IdOrdItem for $type {
106            type Key<'a>
107                = T::Key<'a>
108            where
109                Self: 'a;
110
111            fn key(&self) -> Self::Key<'_> {
112                (**self).key()
113            }
114
115            fn upcast_key<'short, 'long: 'short>(
116                long: Self::Key<'long>,
117            ) -> Self::Key<'short> {
118                T::upcast_key(long)
119            }
120        }
121    };
122}
123
124impl_for_box!(Box<T>);
125impl_for_box!(Rc<T>);
126impl_for_box!(Arc<T>);