Skip to main content

multi_trait/
null.rs

1// SPDX-License-Identifier: Apache-2.0
2/// Trait for types that have a null/sentinel value.
3///
4/// This trait allows multiformats types to define a "null" or sentinel value,
5/// similar to Option::None but for types where a discriminated union isn't
6/// appropriate. Common examples include null CIDs, null signatures, or
7/// default/uninitialized identifiers.
8///
9/// # Use Cases
10///
11/// - Defining "zero" values for custom ID types
12/// - Creating null/empty multiformats objects
13/// - Sentinel values in data structures
14/// - Default initialization for resource handles
15///
16/// # Thread Safety
17///
18/// This trait is `Send + Sync` safe when implemented on thread-safe types.
19///
20/// # Examples
21///
22/// ```rust
23/// use multi_trait::Null;
24///
25/// #[derive(Debug, PartialEq)]
26/// struct UserId(u64);
27///
28/// impl Null for UserId {
29///     fn null() -> Self {
30///         UserId(0)
31///     }
32///
33///     fn is_null(&self) -> bool {
34///         self.0 == 0
35///     }
36/// }
37///
38/// let null_user = UserId::null();
39/// assert!(null_user.is_null());
40///
41/// let valid_user = UserId(42);
42/// assert!(!valid_user.is_null());
43/// ```
44///
45/// # Implementation Guidelines
46///
47/// The null value should be consistent and deterministic. For a given type:
48/// - `T::null()` should always return the same logical value
49/// - `T::null().is_null()` should always return `true`
50/// - The null value should be a valid instance of the type
51pub trait Null {
52    /// Create a null/sentinel value of this type.
53    ///
54    /// This should return a deterministic "null" value that satisfies
55    /// `is_null()`. The exact meaning of "null" is type-specific.
56    ///
57    /// # Examples
58    ///
59    /// ```rust
60    /// use multi_trait::Null;
61    ///
62    /// # struct ResourceId(u32);
63    /// # impl Null for ResourceId {
64    /// #     fn null() -> Self { ResourceId(0) }
65    /// #     fn is_null(&self) -> bool { self.0 == 0 }
66    /// # }
67    /// let null_id = ResourceId::null();
68    /// assert!(null_id.is_null());
69    /// ```
70    fn null() -> Self;
71
72    /// Check if this value is the null value.
73    ///
74    /// Returns `true` if this instance represents the null/sentinel value,
75    /// `false` otherwise.
76    ///
77    /// # Examples
78    ///
79    /// ```rust
80    /// use multi_trait::Null;
81    ///
82    /// # struct Counter(i32);
83    /// # impl Null for Counter {
84    /// #     fn null() -> Self { Counter(0) }
85    /// #     fn is_null(&self) -> bool { self.0 == 0 }
86    /// # }
87    /// let zero = Counter::null();
88    /// assert!(zero.is_null());
89    ///
90    /// let non_zero = Counter(5);
91    /// assert!(!non_zero.is_null());
92    /// ```
93    fn is_null(&self) -> bool;
94}
95
96/// Fallible version of [`Null`] for types where null value creation can fail.
97///
98/// This trait is useful when:
99/// - Null value creation requires allocation
100/// - Validation is needed during null value construction
101/// - Construction can fail in constrained environments
102/// - External resources are needed to create the null value
103///
104/// # Relationship to `Null`
105///
106/// While [`Null`] provides infallible null value creation, `TryNull` handles
107/// cases where construction might fail. If your type's null value is always
108/// constructible, prefer implementing [`Null`] instead.
109///
110/// # Thread Safety
111///
112/// This trait is `Send + Sync` safe when implemented on thread-safe types
113/// with thread-safe error types.
114///
115/// # Examples
116///
117/// ```rust
118/// use multi_trait::TryNull;
119///
120/// #[derive(Debug)]
121/// struct BufferId(Vec<u8>);
122///
123/// impl TryNull for BufferId {
124///     type Error = std::collections::TryReserveError;
125///
126///     fn try_null() -> Result<Self, Self::Error> {
127///         let mut vec = Vec::new();
128///         vec.try_reserve(1)?;
129///         vec.push(0);
130///         Ok(BufferId(vec))
131///     }
132///
133///     fn is_null(&self) -> bool {
134///         self.0.len() == 1 && self.0[0] == 0
135///     }
136/// }
137///
138/// match BufferId::try_null() {
139///     Ok(null_buf) => assert!(null_buf.is_null()),
140///     Err(e) => eprintln!("Failed to create null buffer: {}", e),
141/// }
142/// ```
143///
144/// # Implementation Guidelines
145///
146/// - The error type should describe why null creation failed
147/// - If `try_null()` succeeds, the result should satisfy `is_null()`
148/// - The null value should be consistent across successful calls
149pub trait TryNull: Sized {
150    /// The error type returned when null value creation fails.
151    type Error;
152
153    /// Try to construct a null/sentinel value of this type.
154    ///
155    /// Returns `Ok` with a null value if successful, or `Err` if null value
156    /// creation failed (e.g., due to allocation failure or validation error).
157    ///
158    /// # Errors
159    ///
160    /// Returns an error if null value construction fails. The exact error
161    /// conditions are type-specific.
162    ///
163    /// # Examples
164    ///
165    /// ```rust
166    /// use multi_trait::TryNull;
167    ///
168    /// # struct ValidatedId(String);
169    /// # impl TryNull for ValidatedId {
170    /// #     type Error = &'static str;
171    /// #     fn try_null() -> Result<Self, Self::Error> {
172    /// #         Ok(ValidatedId(String::new()))
173    /// #     }
174    /// #     fn is_null(&self) -> bool { self.0.is_empty() }
175    /// # }
176    /// let null_id = ValidatedId::try_null()?;
177    /// assert!(null_id.is_null());
178    /// # Ok::<(), &'static str>(())
179    /// ```
180    fn try_null() -> Result<Self, Self::Error>;
181
182    /// Check if this value is the null value.
183    ///
184    /// Returns `true` if this instance represents the null/sentinel value,
185    /// `false` otherwise. This method is identical to [`Null::is_null`].
186    ///
187    /// # Examples
188    ///
189    /// ```rust
190    /// use multi_trait::TryNull;
191    ///
192    /// # struct OptionalData(Option<Vec<u8>>);
193    /// # impl TryNull for OptionalData {
194    /// #     type Error = std::string::String;
195    /// #     fn try_null() -> Result<Self, Self::Error> {
196    /// #         Ok(OptionalData(None))
197    /// #     }
198    /// #     fn is_null(&self) -> bool { self.0.is_none() }
199    /// # }
200    /// let data = OptionalData::try_null().unwrap();
201    /// assert!(data.is_null());
202    /// ```
203    fn is_null(&self) -> bool;
204}