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}