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}