wiwi/nominal.rs
1//! Utilities for nominal typing
2
3use crate::prelude::*;
4
5/// Declare a new nominal type (alias), with the provided name, a name for the
6/// marker type struct, and the wrapped type
7///
8/// The returned type alias will be of a guaranteed unique type. This is done by
9/// creating a new ZST with the provided marker type struct name.
10///
11/// The name of the new marker type struct is something we hope to eventually be
12/// able to generate automatically from the given newtype name. If there is a way,
13/// we don't know how >~<
14///
15/// # Examples
16///
17/// Basic usage:
18///
19/// ```
20/// # use wiwi::nominal::{ Nominal, nominal };
21/// // type NewType = Nominal<String, NewTypeMarker>;
22/// nominal!(NewType, marker: NewTypeMarker, wraps: String);
23///
24/// // these two are identical
25/// let item: NewType = NewType::new(String::new());
26/// let item: Nominal<String, NewTypeMarker> = Nominal::new(String::new());
27///
28/// // and of course, it's a type alias
29/// let item: NewType = Nominal::new(String::new());
30/// let item: Nominal<String, NewTypeMarker> = NewType::new(String::new());
31/// ```
32///
33/// The macro does indeed create a unique newtype:
34///
35/// ```compile_fail
36/// # // TODO: use err code E0308 (currently nightly only)
37/// # use wiwi::nominal::nominal;
38/// # nominal!(NewType, marker: NewTypeMarker, wraps: String);
39/// nominal!(AnotherNewType, marker: AnotherNewTypeMarker, wraps: String);
40///
41/// let item: NewType = NewType::new(String::new());
42/// // this won't compile
43/// let another_item: NewType = AnotherNewType::new(String::new());
44/// ```
45///
46/// Controlling visibility of the type alias / marker struct:
47///
48/// ```
49/// mod inner {
50/// # use wiwi::nominal::nominal;
51/// nominal!(pub NewType, marker: NewTypeMarker, wraps: String);
52/// // ↑
53/// }
54///
55/// let item = inner::NewType::new(String::new());
56/// ```
57///
58/// Private visibility is default (like the rest of Rust visibilities):
59///
60/// ```compile_fail
61/// # // TODO: use err code E0603 (currently nightly only)
62/// mod inner {
63/// # use wiwi::nominal::nominal;
64/// nominal!(NewType, marker: NewTypeMarker, wraps: String);
65/// // ↑ no `pub`
66/// }
67///
68/// // this won't compile
69/// let item = inner::NewType::new(String::new());
70/// ```
71///
72/// Other visibilities work too, of course:
73///
74/// ```compile_fail
75/// # // TODO: use err code E0603 (currently nightly only)
76/// mod outer {
77/// mod inner {
78/// # // pag
79/// # use wiwi::nominal::nominal;
80/// nominal!(pub(super) NewType, marker: NewTypeMarker, wraps: String);
81/// }
82///
83/// # fn _maybe_this_fn_decl_shouldnt_be_hidden_i_dont_know() {
84/// // this is fine...
85/// let item = inner::NewType::new(String::new());
86/// # }
87/// }
88///
89/// // but this won't compile
90/// let item = outer::inner::NewType::new(String::new());
91/// ```
92#[macro_export]
93macro_rules! nominal {
94 ($vis:vis $name:ident, marker: $marker:ident, wraps: $( ref <$($lifetimes:lifetime),+> )? $ty:ty) => {
95 $vis struct $marker;
96 $vis type $name$(<$($lifetimes),+>)? = $crate::nominal::Nominal<$ty, $marker>;
97 };
98}
99pub use nominal;
100
101/// Declare many new nominal types (aliases), in a module
102///
103/// Usage is more or less identical to [`nominal`], but you define a module
104/// inside the macro invocation. Because this macro creates a new module
105/// (with the name you specify), and the created module is only used for defining
106/// these
107/// nominal types, there can be nothing else in there, which we take advantage of
108/// to create a `marker` submodule to define marker types in. This way it can have
109/// a new namespace just for the marker types, so reusing the newtype name won't
110/// collide with anything else.
111///
112/// So, all of that is to say this macro also saves you a bit of boilerplate
113/// declaring names for the newtype ZSTs.
114///
115/// # Examples
116///
117/// ```
118/// # use wiwi::nominal::nominal_mod;
119/// nominal_mod! {
120/// pub mod nominal {
121/// nominal!(pub NewType, wraps: String);
122/// }
123/// }
124///
125/// let item = nominal::NewType::new(String::new());
126/// ```
127///
128/// Still creating newtypes as expected:
129///
130/// ```compile_fail
131/// # // TODO: use err code E0308 (currently nightly only)
132/// # use wiwi::nominal::nominal_mod;
133/// nominal_mod! {
134/// pub mod nominal {
135/// nominal!(pub NewType, wraps: String);
136/// nominal!(pub AnotherNewType, wraps: String);
137/// }
138/// }
139///
140/// let item: nominal::NewType = nominal::NewType::new(String::new());
141/// // this won't compile
142/// let another_item: nominal::NewType = nominal::AnotherNewType::new(String::new());
143/// ```
144///
145/// Still "just" a type alias:
146///
147/// ```
148/// # use wiwi::nominal::{ Nominal, nominal_mod };
149/// # nominal_mod! {
150/// # pub mod nominal {
151/// # nominal!(pub NewType, wraps: String);
152/// # }
153/// # }
154///
155/// let item: nominal::NewType = Nominal::new(String::new());
156/// ```
157///
158/// Created marker structs are in a `marker` submodule:
159///
160/// ```
161/// # use wiwi::nominal::{ Nominal, nominal_mod };
162/// # nominal_mod! {
163/// # pub mod nominal {
164/// # nominal!(pub NewType, wraps: String);
165/// # nominal!(pub AnotherNewType, wraps: String);
166/// # }
167/// # }
168///
169/// let item: Nominal<String, nominal::marker::NewType> = nominal::NewType::new(String::new());
170/// let item: Nominal<String, nominal::marker::AnotherNewType> = nominal::AnotherNewType::new(String::new());
171/// ```
172#[macro_export]
173macro_rules! nominal_mod {
174 {
175 $(
176 $mod_vis:vis mod $mod_name:ident {
177 $( nominal!($item_vis:vis $name:ident, wraps: $( ref <$($lifetimes:lifetime),+> )? $type:ty); )*
178 }
179 )*
180 } => {
181 $(
182 $mod_vis mod $mod_name {
183 pub mod marker {
184 $( pub struct $name; )*
185 }
186
187 use super::*;
188 $( $item_vis type $name$( <$($lifetimes),+> )? = $crate::nominal::Nominal<$type, marker::$name>; )*
189 }
190 )*
191 }
192}
193pub use nominal_mod;
194
195/// Nominal wrapper struct
196///
197/// This struct is zero cost; it is simply a type safe wrapper.
198///
199/// Newtypes are primarily created with assistance from the [`nominal`] and
200/// [`nominal_mod`] macros. The macros will help save you the boilerplate of
201/// writing the types and declaring unit structs to use as the marker.
202#[repr(transparent)]
203pub struct Nominal<T, M> {
204 item: T,
205 marker: PhantomData<M>
206}
207
208impl<T, M> Nominal<T, M> {
209 /// Creates a nominal struct with the given value
210 #[inline]
211 pub fn new(item: T) -> Self {
212 Self { item, marker: PhantomData }
213 }
214
215 /// Unwraps the nominal struct and returns the value
216 #[inline]
217 pub fn into_inner(self) -> T {
218 self.item
219 }
220
221 /// Gets a reference to the wrapped value
222 ///
223 /// Note: [`Deref`] is not implemented on purpose, to prevent
224 /// unintentional auto-derefs
225 // TODO: should we reconsider the above?
226 #[inline]
227 pub fn as_value_ref(&self) -> &T {
228 &self.item
229 }
230
231 /// Gets a mut reference to the wrapped value
232 ///
233 /// Note: [`DerefMut`] is not implemented on purpose, to prevent
234 /// unintentional auto-derefs
235 // TODO: should we reconsider the above?
236 #[inline]
237 pub fn as_value_mut(&mut self) -> &mut T {
238 &mut self.item
239 }
240
241 /// Unwraps and rewraps the value as another nominal type, without modifying
242 /// the wrapped value
243 ///
244 /// If you're using this function, make sure you know why you're using it!
245 /// after all, the whole point of this is to seperate otherwise identical
246 /// types into newtypes based on semantic meaning.
247 #[inline]
248 pub fn with_other_marker<M2>(self) -> Nominal<T, M2> {
249 Nominal::new(self.into_inner())
250 }
251
252 /// Consumes and maps the wrapped value into another value, wrapping it in
253 /// a nominal type with the same marker
254 #[inline]
255 pub fn map<T2, F>(self, f: F) -> Nominal<T2, M>
256 where
257 F: FnOnce(T) -> T2
258 {
259 Nominal::new(f(self.into_inner()))
260 }
261
262 /// Consumes and asyncronously maps the wrapped value into another value,
263 /// wrapping it in a nominal type with the same marker
264 #[inline]
265 pub async fn map_async<T2, F, Fu>(self, f: F) -> Nominal<T2, M>
266 where
267 F: FnOnce(T) -> Fu,
268 Fu: IntoFuture<Output = T2>
269 {
270 Nominal::new(f(self.into_inner()).await)
271 }
272}
273
274impl<T, M, E> Nominal<Result<T, E>, M> {
275 /// Transpose a nominal wrapped [`Result`] into a [`Result`] of a nominal
276 /// wrapped value
277 ///
278 /// The value gets wrapped, but the error does not. Both are not otherwise
279 /// modified in any way.
280 #[inline]
281 pub fn transpose(self) -> Result<Nominal<T, M>, E> {
282 self.into_inner().map(Nominal::new)
283 }
284
285 /// Maps the [`Ok`] value of a [`Result`], wrapping the resulting [`Result`]
286 /// in a nominal type with the same marker
287 #[inline]
288 pub fn map_result_ok<T2, F>(self, f: F) -> Nominal<Result<T2, E>, M>
289 where
290 F: FnOnce(T) -> T2
291 {
292 Nominal::new(self.into_inner().map(f))
293 }
294
295 /// Maps the [`Err`] value of a [`Result`], wrapping the resulting [`Result`]
296 /// in a nominal type with the same marker
297 #[inline]
298 pub fn map_result_err<E2, F>(self, f: F) -> Nominal<Result<T, E2>, M>
299 where
300 F: FnOnce(E) -> E2
301 {
302 Nominal::new(self.into_inner().map_err(f))
303 }
304}
305
306impl<T, M> Nominal<Option<T>, M> {
307 /// Transpose a nominal wrapped [`Option`] into an [`Option`] of a nominal
308 /// wrapped value
309 ///
310 /// The value is not otherwise modified in any way.
311 #[inline]
312 pub fn transpose(self) -> Option<Nominal<T, M>> {
313 self.into_inner().map(Nominal::new)
314 }
315
316 /// Maps the [`Some`] value of an [`Option`], wrapping the resulting [`Option`]
317 /// in a nominal type with the same marker
318 #[inline]
319 pub fn map_option_some<T2, F>(self, f: F) -> Nominal<Option<T2>, M>
320 where
321 F: FnOnce(T) -> T2
322 {
323 Nominal::new(self.into_inner().map(f))
324 }
325}
326
327impl<T, M> From<T> for Nominal<T, M> {
328 #[inline]
329 fn from(value: T) -> Self {
330 Self::new(value)
331 }
332}
333
334// delegate trait impls by just calling T's impl
335
336impl<T, M> Clone for Nominal<T, M>
337where
338 T: Clone
339{
340 #[inline]
341 fn clone(&self) -> Self {
342 Self::new(self.as_value_ref().clone())
343 }
344
345 #[inline]
346 fn clone_from(&mut self, source: &Self) {
347 self.as_value_mut().clone_from(source.as_value_ref())
348 }
349}
350
351impl<T, M> Copy for Nominal<T, M>
352where
353 T: Copy
354{}
355
356impl<T, M> Debug for Nominal<T, M>
357where
358 T: Debug
359{
360 #[inline]
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 f.debug_struct("Nominal")
363 .field("value", self.as_value_ref())
364 .finish()
365 }
366}
367
368impl<T, M> Display for Nominal<T, M>
369where
370 T: Display
371{
372 #[inline]
373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374 self.as_value_ref().fmt(f)
375 }
376}
377
378impl<T, M> Default for Nominal<T, M>
379where
380 T: Default
381{
382 #[inline]
383 fn default() -> Self {
384 Self::new(T::default())
385 }
386}
387
388impl<T, M> Hash for Nominal<T, M>
389where
390 T: Hash
391{
392 #[inline]
393 fn hash<H>(&self, state: &mut H)
394 where
395 H: Hasher
396 {
397 self.as_value_ref().hash(state)
398 }
399
400 #[inline]
401 fn hash_slice<H>(data: &[Self], state: &mut H)
402 where
403 Self: Sized,
404 H: Hasher
405 {
406 #[expect(clippy::as_conversions, reason = "ptr cast")]
407 let slice_ptr = data as *const [Self] as *const [T];
408
409 // SAFETY:
410 // - we're repr(transparent)
411 // - reborrowing `data` as slice of `T`
412 let slice = unsafe { &*slice_ptr };
413
414 T::hash_slice(slice, state)
415 }
416}
417
418impl<T, M, TR, MR> PartialEq<Nominal<TR, MR>> for Nominal<T, M>
419where
420 T: PartialEq<TR>
421{
422 #[inline]
423 fn eq(&self, other: &Nominal<TR, MR>) -> bool {
424 self.as_value_ref().eq(other.as_value_ref())
425 }
426
427 #[expect(
428 clippy::partialeq_ne_impl,
429 reason = "T might have overridden `ne`. we should use it if so"
430 )]
431 #[inline]
432 fn ne(&self, other: &Nominal<TR, MR>) -> bool {
433 self.as_value_ref().ne(other.as_value_ref())
434 }
435}
436
437impl<T, M> Eq for Nominal<T, M>
438where
439 T: Eq
440{}
441
442impl<T, M, TR, MR> PartialOrd<Nominal<TR, MR>> for Nominal<T, M>
443where
444 T: PartialOrd<TR>
445{
446 #[inline]
447 fn partial_cmp(&self, other: &Nominal<TR, MR>) -> Option<cmp::Ordering> {
448 self.as_value_ref().partial_cmp(other.as_value_ref())
449 }
450
451 #[inline]
452 fn lt(&self, other: &Nominal<TR, MR>) -> bool {
453 self.as_value_ref().lt(other.as_value_ref())
454 }
455
456 #[inline]
457 fn le(&self, other: &Nominal<TR, MR>) -> bool {
458 self.as_value_ref().le(other.as_value_ref())
459 }
460
461 #[inline]
462 fn gt(&self, other: &Nominal<TR, MR>) -> bool {
463 self.as_value_ref().gt(other.as_value_ref())
464 }
465
466 #[inline]
467 fn ge(&self, other: &Nominal<TR, MR>) -> bool {
468 self.as_value_ref().ge(other.as_value_ref())
469 }
470}
471
472impl<T, M> Ord for Nominal<T, M>
473where
474 T: Ord
475{
476 #[inline]
477 fn cmp(&self, other: &Self) -> cmp::Ordering {
478 self.as_value_ref().cmp(other.as_value_ref())
479 }
480
481 #[inline]
482 fn max(self, other: Self) -> Self {
483 Self::new(self.into_inner().max(other.into_inner()))
484 }
485
486 #[inline]
487 fn min(self, other: Self) -> Self {
488 Self::new(self.into_inner().min(other.into_inner()))
489 }
490
491 #[inline]
492 fn clamp(self, min: Self, max: Self) -> Self {
493 Self::new(self.into_inner().clamp(min.into_inner(), max.into_inner()))
494 }
495}