orx_pseudo_default/
pseudo_default.rs

1/// `PseudoDefault` trait allows to create a cheap default instance of a type, which **does not claim to be useful**.
2///
3/// The difference of `PseudoDefault` from `Default` is the relaxed expectation of the created instance to be useful.
4///
5/// The main use case of the trait is when we need to create a cheap instance of a type without any arguments, only to throw away afterwards. Therefore, created instance does not need to be a decent one.
6///
7/// This trait allows to avoid unsafe code in certain use cases. For instance:
8/// * We can avoid tricks such as uninit, manually-drop, etc. that requires to be extremely careful, when we could've actually created a valid instance much more easily.
9/// * We can use pseudo-default to fill the gaps when we need to take out an element from a collection of types that cannot implement Default.
10///
11/// Note that pseudo-default requirement is more relaxed than that of default, and hence,
12/// * types implementing Default can implement PseudoDefault,
13/// * additionally, types that cannot implement Default can manually implement PseudoDefault, provided that it is safe and cheap to create a pseudo instance of the type without any arguments.
14///
15/// # Example
16///
17/// Consider the following fictional type `Share` which divides a whole into pieces. Without providing the `number_of_shares`, this type does not have a meaning.
18///
19/// **Therefore, we cannot justify implementing `Default`, it would be misleading.**
20///
21/// If we still need to be able to create Share's for some reason, we can simply use `pseudo_default`. We would know that the created type does not promise to make sense behaviorally; however, it is still a cheap and valid instance that can be safely dropped.
22///
23/// ```rust
24/// use orx_pseudo_default::PseudoDefault;
25///
26/// struct Share {
27///     number_of_shares: std::num::NonZeroUsize,
28/// }
29///
30/// impl Share {
31///     fn share_size(&self, whole_amount: usize) -> usize {
32///         whole_amount / self.number_of_shares
33///     }
34/// }
35///
36/// impl PseudoDefault for Share {
37///     fn pseudo_default() -> Self {
38///         Self {
39///             number_of_shares: std::num::NonZeroUsize::new(1).unwrap(),
40///         }
41///     }
42/// }
43/// ```
44///
45/// A more advanced use case could be the following. Assume that we are trying to create a vec wrapper called `TakeVec` with the following features;
46/// * it allows to take out elements by index by a method called `take`
47/// * we should be able to wrap an allocated vec without any additional allocation
48/// * we need to be able to give back the originally allocated vec
49/// * we want to achieve this without unsafe code
50///
51/// It is trivial to implement this with `Default` but we want to be less restrictive on the constraint so that it works for non-default types as well. We can use PseudoDefault for this.
52///
53/// ```rust
54/// use orx_pseudo_default::PseudoDefault;
55/// # struct Share {
56/// #     number_of_shares: std::num::NonZeroUsize,
57/// # }
58/// #
59/// # impl Share {
60/// #     fn share_size(&self, whole_amount: usize) -> usize {
61/// #         whole_amount / self.number_of_shares
62/// #     }
63/// # }
64/// #
65/// # impl PseudoDefault for Share {
66/// #     fn pseudo_default() -> Self {
67/// #         Self {
68/// #             number_of_shares: std::num::NonZeroUsize::new(1).unwrap(),
69/// #         }
70/// #     }
71/// # }
72/// struct TakeVec<T>(Vec<T>);
73///
74/// impl<T> From<Vec<T>> for TakeVec<T> {
75///     fn from(inner: Vec<T>) -> Self {
76///         Self(inner)
77///     }
78/// }
79///
80/// impl<T> From<TakeVec<T>> for Vec<T> {
81///     fn from(value: TakeVec<T>) -> Self {
82///         value.0
83///     }
84/// }
85///
86/// impl<T: PseudoDefault> TakeVec<T> {
87///     fn take(&mut self, index: usize) -> Option<T> {
88///         self.0.get_mut(index).map(|element| {
89///             let mut value = T::pseudo_default();
90///             std::mem::swap(&mut value, element);
91///             value
92///         })
93///     }
94/// }
95///
96/// // implemented default types
97///
98/// let mut vec: TakeVec<_> = vec![0, 1, 2, 3].into();
99/// assert_eq!(vec.take(2), Some(2));
100///
101/// let mut vec: TakeVec<_> = vec![0.to_string(), 1.to_string()].into();
102/// assert_eq!(vec.take(0), Some(String::from("0")));
103///
104/// // non-default types
105///
106/// let mut vec: TakeVec<_> = vec![
107///     Share {
108///         number_of_shares: std::num::NonZeroUsize::new(42).unwrap(),
109///     },
110///     Share {
111///         number_of_shares: std::num::NonZeroUsize::new(7).unwrap(),
112///     },
113/// ]
114/// .into();
115/// assert_eq!(vec.take(0).map(|x| x.number_of_shares.into()), Some(42));
116/// ```
117pub trait PseudoDefault {
118    /// `PseudoDefault` trait allows to create a cheap default instance of a type, which **does not claim to be useful**.
119    ///
120    /// The difference of `PseudoDefault` from `Default` is the relaxed expectation of the created instance to be useful.
121    ///
122    /// The main use case of the trait is when we need to create a cheap instance of a type without any arguments, only to throw away afterwards. Therefore, created instance does not need to be a decent one.
123    ///
124    /// This trait allows to avoid unsafe code in certain use cases. For instance:
125    /// * We can avoid tricks such as uninit, manually-drop, etc. that requires to be extremely careful, when we could've actually created a valid instance much more easily.
126    /// * We can use pseudo-default to fill the gaps when we need to take out an element from a collection of types that cannot implement Default.
127    ///
128    /// Note that pseudo-default requirement is more relaxed than that of default, and hence,
129    /// * types implementing Default can implement PseudoDefault,
130    /// * additionally, types that cannot implement Default can manually implement PseudoDefault, provided that it is safe and cheap to create a pseudo instance of the type without any arguments.
131    ///
132    /// # Example
133    ///
134    /// Consider the following fictional type `Share` which divides a whole into pieces. Without providing the `number_of_shares`, this type does not have a meaning.
135    ///
136    /// **Therefore, we cannot justify implementing `Default`, it would be misleading.**
137    ///
138    /// If we still need to be able to create Share's for some reason, we can simply use `pseudo_default`. We would know that the created type does not promise to make sense behaviorally; however, it is still a cheap and valid instance that can be safely dropped.
139    ///
140    /// ```rust
141    /// use orx_pseudo_default::PseudoDefault;
142    ///
143    /// struct Share {
144    ///     number_of_shares: std::num::NonZeroUsize,
145    /// }
146    ///
147    /// impl Share {
148    ///     fn share_size(&self, whole_amount: usize) -> usize {
149    ///         whole_amount / self.number_of_shares
150    ///     }
151    /// }
152    ///
153    /// impl PseudoDefault for Share {
154    ///     fn pseudo_default() -> Self {
155    ///         Self {
156    ///             number_of_shares: std::num::NonZeroUsize::new(1).unwrap(),
157    ///         }
158    ///     }
159    /// }
160    /// ```
161    ///
162    /// A more advanced use case could be the following. Assume that we are trying to create a vec wrapper called `TakeVec` with the following features;
163    /// * it allows to take out elements by index by a method called `take`
164    /// * we should be able to wrap an allocated vec without any additional allocation
165    /// * we need to be able to give back the originally allocated vec
166    /// * we want to achieve this without unsafe code
167    ///
168    /// It is trivial to implement this with `Default` but we want to be less restrictive on the constraint so that it works for non-default types as well. We can use PseudoDefault for this.
169    ///
170    /// ```rust
171    /// use orx_pseudo_default::PseudoDefault;
172    /// # struct Share {
173    /// #     number_of_shares: std::num::NonZeroUsize,
174    /// # }
175    /// #
176    /// # impl Share {
177    /// #     fn share_size(&self, whole_amount: usize) -> usize {
178    /// #         whole_amount / self.number_of_shares
179    /// #     }
180    /// # }
181    /// #
182    /// # impl PseudoDefault for Share {
183    /// #     fn pseudo_default() -> Self {
184    /// #         Self {
185    /// #             number_of_shares: std::num::NonZeroUsize::new(1).unwrap(),
186    /// #         }
187    /// #     }
188    /// # }
189    /// struct TakeVec<T>(Vec<T>);
190    ///
191    /// impl<T> From<Vec<T>> for TakeVec<T> {
192    ///     fn from(inner: Vec<T>) -> Self {
193    ///         Self(inner)
194    ///     }
195    /// }
196    ///
197    /// impl<T> From<TakeVec<T>> for Vec<T> {
198    ///     fn from(value: TakeVec<T>) -> Self {
199    ///         value.0
200    ///     }
201    /// }
202    ///
203    /// impl<T: PseudoDefault> TakeVec<T> {
204    ///     fn take(&mut self, index: usize) -> Option<T> {
205    ///         self.0.get_mut(index).map(|element| {
206    ///             let mut value = T::pseudo_default();
207    ///             std::mem::swap(&mut value, element);
208    ///             value
209    ///         })
210    ///     }
211    /// }
212    ///
213    /// // implemented default types
214    ///
215    /// let mut vec: TakeVec<_> = vec![0, 1, 2, 3].into();
216    /// assert_eq!(vec.take(2), Some(2));
217    ///
218    /// let mut vec: TakeVec<_> = vec![0.to_string(), 1.to_string()].into();
219    /// assert_eq!(vec.take(0), Some(String::from("0")));
220    ///
221    /// // non-default types
222    ///
223    /// let mut vec: TakeVec<_> = vec![
224    ///     Share {
225    ///         number_of_shares: std::num::NonZeroUsize::new(42).unwrap(),
226    ///     },
227    ///     Share {
228    ///         number_of_shares: std::num::NonZeroUsize::new(7).unwrap(),
229    ///     },
230    /// ]
231    /// .into();
232    /// assert_eq!(vec.take(0).map(|x| x.number_of_shares.into()), Some(42));
233    /// ```
234    fn pseudo_default() -> Self;
235}