rancor/lib.rs
1//! # rancor
2//!
3//! rancor provides scalable and efficient error handling without using type
4//! composition. This makes it best-suited for situations where:
5//!
6//! - Programmatic error introspection is not useful
7//! - Functions may error, but succeed most of the time
8//! - Errors should provide as much useful detail as possible when emitted
9//! - Use cases include both `no_std` and targets with support for `std`
10//!
11//! ## Features
12//!
13//! - `alloc`: Provides the [`BoxedError`] type. Enabled by default.
14
15#![deny(
16 future_incompatible,
17 missing_docs,
18 nonstandard_style,
19 unsafe_op_in_unsafe_fn,
20 unused,
21 warnings,
22 clippy::all,
23 clippy::missing_safety_doc,
24 clippy::undocumented_unsafe_blocks,
25 rustdoc::broken_intra_doc_links,
26 rustdoc::missing_crate_level_docs
27)]
28#![no_std]
29#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg))]
30
31#[cfg(feature = "alloc")]
32extern crate alloc;
33
34#[cfg(feature = "alloc")]
35mod boxed_error;
36#[cfg(feature = "alloc")]
37mod thin_box;
38
39use core::{
40 error, fmt,
41 hint::unreachable_unchecked,
42 marker::PhantomData,
43 ops::{Deref, DerefMut},
44};
45
46/// An error type which can add additional "trace" information to itself.
47///
48/// Some functions only add additional context to errors created by other
49/// functions, rather than creating errors themselves. With generics, it's
50/// therefore possible to have a generic function which can produce errors with
51/// some type arguments but not with others. In these cases, `Trace` allows
52/// those functions to add context if an error can occur, and compile out the
53/// context if the error type is [`Infallible`] or [`Panic`].
54///
55/// # Example
56///
57/// ```
58/// use rancor::{ResultExt, Trace};
59///
60/// trait Print<E> {
61/// fn print(&self, message: &str) -> Result<(), E>;
62/// }
63///
64/// fn print_hello_world<T: Print<E>, E: Trace>(printer: &T) -> Result<(), E> {
65/// printer.print("hello").trace("failed to print hello")?;
66/// printer.print("world").trace("failed to print world")?;
67/// Ok(())
68/// }
69/// ```
70pub trait Trace: Sized + Send + Sync + 'static {
71 /// Adds an additional trace to this error, returning a new error.
72 fn trace<R>(self, trace: R) -> Self
73 where
74 R: fmt::Debug + fmt::Display + Send + Sync + 'static;
75}
76
77/// An error type which can be uniformly constructed from an [`Error`] and
78/// additional trace information.
79///
80/// # Example
81///
82/// ```
83/// use core::{error::Error, fmt};
84///
85/// use rancor::{fail, Source};
86///
87/// #[derive(Debug)]
88/// struct DivideByZeroError;
89///
90/// impl fmt::Display for DivideByZeroError {
91/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92/// write!(f, "attempted to divide by zero")
93/// }
94/// }
95///
96/// impl Error for DivideByZeroError {}
97///
98/// fn try_divide<E: Source>(a: i32, b: i32) -> Result<i32, E> {
99/// if b == 0 {
100/// fail!(DivideByZeroError);
101/// }
102/// Ok(a / b)
103/// }
104/// ```
105pub trait Source: Trace + error::Error {
106 /// Returns a new `Self` using the given [`Error`].
107 ///
108 /// Depending on the specific implementation, this may box the error,
109 /// immediately emit a diagnostic, or discard it and only remember that some
110 /// error occurred.
111 fn new<T: error::Error + Send + Sync + 'static>(source: T) -> Self;
112}
113
114/// A type with fallible operations that return its associated error type.
115///
116/// `Fallible` turns an error type parameter into an associated type of another
117/// parameter. You can equip an existing type with a `Fallible` implementation
118/// by wrapping it in a [`Strategy`].
119///
120/// # Example
121///
122/// ```
123/// use rancor::{Failure, Fallible, Strategy};
124///
125/// trait Operator<E = <Self as Fallible>::Error> {
126/// fn operate(&self, lhs: i32, rhs: i32) -> Result<i32, E>;
127/// }
128///
129/// impl<T: Operator<E> + ?Sized, E> Operator<E> for Strategy<T, E> {
130/// fn operate(&self, lhs: i32, rhs: i32) -> Result<i32, E> {
131/// T::operate(self, lhs, rhs)
132/// }
133/// }
134///
135/// struct Add;
136///
137/// impl<E> Operator<E> for Add {
138/// fn operate(&self, lhs: i32, rhs: i32) -> Result<i32, E> {
139/// Ok(lhs + rhs)
140/// }
141/// }
142///
143/// fn operate_one_one<T: Operator + Fallible>(
144/// operator: &T,
145/// ) -> Result<i32, T::Error> {
146/// operator.operate(1, 1)
147/// }
148///
149/// assert_eq!(
150/// operate_one_one(Strategy::<_, Failure>::wrap(&mut Add)),
151/// Ok(2)
152/// );
153/// ```
154pub trait Fallible {
155 /// The error type associated with this type's operations.
156 type Error;
157}
158
159/// Equips a type with a `Fallible` implementation that chooses `E` as its error
160/// type.
161///
162/// # Example
163///
164/// ```
165/// use rancor::{Failure, Fallible, Strategy};
166///
167/// trait Print<E = <Self as Fallible>::Error> {
168/// fn print(&self, message: &str) -> Result<(), E>;
169/// }
170///
171/// impl<T: Print<E> + ?Sized, E> Print<E> for Strategy<T, E> {
172/// fn print(&self, message: &str) -> Result<(), E> {
173/// T::print(self, message)
174/// }
175/// }
176///
177/// struct StdOut;
178///
179/// impl<E> Print<E> for StdOut {
180/// fn print(&self, message: &str) -> Result<(), E> {
181/// println!("{message}");
182/// Ok(())
183/// }
184/// }
185///
186/// Strategy::<_, Failure>::wrap(&mut StdOut)
187/// .print("hello world")
188/// .unwrap();
189/// ```
190#[repr(transparent)]
191pub struct Strategy<T: ?Sized, E> {
192 _error: PhantomData<E>,
193 inner: T,
194}
195
196impl<T: ?Sized, E> Fallible for Strategy<T, E> {
197 type Error = E;
198}
199
200impl<T: ?Sized, E> Strategy<T, E> {
201 /// Wraps the given mutable reference, returning a mutable reference to a
202 /// `Strategy`.
203 ///
204 /// ## Example
205 /// ```
206 /// use core::ops::Deref;
207 ///
208 /// use rancor::{Failure, Strategy};
209 /// fn test() {
210 /// struct Inner {
211 /// value: u64,
212 /// }
213 ///
214 /// let mut inner = Inner { value: 10 };
215 ///
216 /// let inner_value_ptr = &inner.value as *const u64;
217 /// let strategy: &mut Strategy<Inner, Failure> =
218 /// Strategy::wrap(&mut inner);
219 /// let strategy_value_ptr = (&strategy.deref().value) as *const u64;
220 /// assert_eq!(inner_value_ptr, strategy_value_ptr);
221 /// // Strategy wraps a type but does not change its memory layout.
222 /// }
223 ///
224 /// test();
225 /// ```
226 pub fn wrap(inner: &mut T) -> &mut Self {
227 // SAFETY: `Strategy` is `repr(transparent)` and so has the same layout
228 // as `T`. The input and output lifetimes are the same, so mutable
229 // aliasing rules will be upheld. Finally, because the inner `T` is the
230 // final element of `Strategy`, the pointer metadata of the two pointers
231 // will be the same.
232 unsafe { core::mem::transmute::<&mut T, &mut Self>(inner) }
233 }
234}
235
236impl<T: ?Sized, E> Deref for Strategy<T, E> {
237 type Target = T;
238
239 fn deref(&self) -> &Self::Target {
240 &self.inner
241 }
242}
243
244impl<T: ?Sized, E> DerefMut for Strategy<T, E> {
245 fn deref_mut(&mut self) -> &mut Self::Target {
246 &mut self.inner
247 }
248}
249
250/// Returns the given error from this function.
251///
252/// The current function must return a `Result<_, E>` where `E` implements
253/// [`Source`].
254///
255/// # Example
256///
257/// ```
258/// use core::{error::Error, fmt};
259///
260/// use rancor::{fail, Source};
261///
262/// #[derive(Debug)]
263/// struct DivideByZeroError;
264///
265/// impl fmt::Display for DivideByZeroError {
266/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267/// write!(f, "attempted to divide by zero")
268/// }
269/// }
270///
271/// impl Error for DivideByZeroError {}
272///
273/// fn divide<E: Source>(a: i32, b: i32) -> Result<i32, E> {
274/// if b == 0 {
275/// fail!(DivideByZeroError);
276/// }
277/// Ok(a / b)
278/// }
279/// ```
280#[macro_export]
281macro_rules! fail {
282 ($($x:tt)*) => {
283 return ::core::result::Result::Err($crate::Source::new($($x)*));
284 };
285}
286
287/// Helper methods for `Result`s.
288pub trait ResultExt<T, E> {
289 /// Returns a `Result` with this error type converted to `U`.
290 ///
291 /// # Example
292 ///
293 /// ```
294 /// use rancor::{Failure, ResultExt as _};
295 ///
296 /// let result = "1_000".parse::<i32>().into_error::<Failure>();
297 /// ```
298 fn into_error<U>(self) -> Result<T, U>
299 where
300 U: Source,
301 E: error::Error + Send + Sync + 'static;
302
303 /// Returns a `Result` with this error type converted to `U` and with an
304 /// additional `trace` message added.
305 ///
306 /// # Example
307 ///
308 /// ```
309 /// use rancor::{BoxedError, ResultExt as _};
310 ///
311 /// let result = "1_000"
312 /// .parse::<i32>()
313 /// .into_trace::<BoxedError, _>("while parsing 1_000");
314 /// ```
315 fn into_trace<U, R>(self, trace: R) -> Result<T, U>
316 where
317 U: Source,
318 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
319 E: error::Error + Send + Sync + 'static;
320
321 /// Returns a `Result` with this error type converted to `U` and with an
322 /// additional trace message added by evaluating the given function `f`. The
323 /// function is evaluated only if an error occurred.
324 ///
325 /// # Example
326 ///
327 /// ```
328 /// use rancor::{BoxedError, ResultExt as _};
329 ///
330 /// let input = "1_000";
331 /// let result =
332 /// input
333 /// .parse::<i32>()
334 /// .into_with_trace::<BoxedError, _, _>(|| {
335 /// format!("while parsing {input}")
336 /// });
337 /// ```
338 fn into_with_trace<U, R, F>(self, f: F) -> Result<T, U>
339 where
340 U: Source,
341 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
342 F: FnOnce() -> R,
343 E: error::Error + Send + Sync + 'static;
344
345 /// Adds an additional `trace` message to the error value of this type.
346 ///
347 /// # Example
348 ///
349 /// ```
350 /// use rancor::{BoxedError, ResultExt as _};
351 ///
352 /// let result = "1_000"
353 /// .parse::<i32>()
354 /// .into_error::<BoxedError>()
355 /// .trace("while parsing 1_000");
356 /// ```
357 fn trace<R>(self, trace: R) -> Result<T, E>
358 where
359 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
360 E: Trace;
361
362 /// Adds an additional trace message to the error value of this type by
363 /// evaluating the given function `f`. The function is evaluated only if an
364 /// error occurred.
365 ///
366 /// # Example
367 ///
368 /// ```
369 /// use rancor::{BoxedError, ResultExt as _};
370 ///
371 /// let input = "1_000";
372 /// let result = input
373 /// .parse::<i32>()
374 /// .into_error::<BoxedError>()
375 /// .with_trace(|| format!("while parsing {input}"));
376 /// ```
377 fn with_trace<R, F>(self, f: F) -> Result<T, E>
378 where
379 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
380 F: FnOnce() -> R,
381 E: Trace;
382
383 /// Safely unwraps a result that is always `Ok`.
384 ///
385 /// In order to call this method, the error type of this `Result` must be a
386 /// [`Never`] type.
387 ///
388 /// # Example
389 ///
390 /// ```
391 /// use rancor::{Infallible, ResultExt};
392 ///
393 /// let inner = Ok::<i32, Infallible>(10).always_ok();
394 /// ```
395 fn always_ok(self) -> T
396 where
397 E: Never;
398}
399
400impl<T, E> ResultExt<T, E> for Result<T, E> {
401 fn into_error<U>(self) -> Result<T, U>
402 where
403 U: Source,
404 E: error::Error + Send + Sync + 'static,
405 {
406 match self {
407 Ok(x) => Ok(x),
408 Err(e) => Err(U::new(e)),
409 }
410 }
411
412 fn into_trace<U, R>(self, trace: R) -> Result<T, U>
413 where
414 U: Source,
415 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
416 E: error::Error + Send + Sync + 'static,
417 {
418 match self {
419 Ok(x) => Ok(x),
420 Err(e) => Err(U::new(e).trace(trace)),
421 }
422 }
423
424 fn into_with_trace<U, R, F>(self, f: F) -> Result<T, U>
425 where
426 U: Source,
427 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
428 F: FnOnce() -> R,
429 E: error::Error + Send + Sync + 'static,
430 {
431 match self {
432 Ok(x) => Ok(x),
433 Err(e) => Err(U::new(e).trace(f())),
434 }
435 }
436
437 fn trace<R>(self, trace: R) -> Result<T, E>
438 where
439 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
440 E: Trace,
441 {
442 match self {
443 Ok(x) => Ok(x),
444 Err(e) => Err(e.trace(trace)),
445 }
446 }
447
448 fn with_trace<R, F>(self, f: F) -> Result<T, E>
449 where
450 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
451 F: FnOnce() -> R,
452 E: Trace,
453 {
454 match self {
455 Ok(x) => Ok(x),
456 Err(e) => Err(e.trace(f())),
457 }
458 }
459
460 fn always_ok(self) -> T
461 where
462 E: Never,
463 {
464 match self {
465 Ok(x) => x,
466 Err(e) => unreachable_checked(e),
467 }
468 }
469}
470
471/// Helper methods for `Option`s.
472pub trait OptionExt<T> {
473 /// Returns a `Result` with an error indicating that `Some` was expected but
474 /// `None` was found.
475 ///
476 /// # Example
477 ///
478 /// ```
479 /// use rancor::{Failure, OptionExt};
480 ///
481 /// let result = Some(10).into_error::<Failure>();
482 /// ```
483 fn into_error<E>(self) -> Result<T, E>
484 where
485 E: Source;
486
487 /// Returns a `Result` with an error indicating that `Some` was expected but
488 /// `None` was found, and with an additional `trace` message added.
489 ///
490 /// # Example
491 ///
492 /// ```
493 /// use rancor::{Failure, OptionExt};
494 ///
495 /// ##[rustfmt::skip]
496 /// let result = Some(10).
497 /// into_trace::<Failure, _>("while converting Some(10)");
498 /// ```
499 fn into_trace<E, R>(self, trace: R) -> Result<T, E>
500 where
501 E: Source,
502 R: fmt::Debug + fmt::Display + Send + Sync + 'static;
503
504 /// Returns a `Result` with an error indicating that `Some` was expected but
505 /// `None` was found, and with an additional trace message added by
506 /// evaluating the given function `f`. The function is evaluated only if an
507 /// error occurred.
508 ///
509 /// # Example
510 ///
511 /// ```
512 /// use rancor::{Failure, OptionExt};
513 ///
514 /// let input = Some(10);
515 /// let result = input.into_with_trace::<Failure, _, _>(|| {
516 /// format!("while converting {input:?}")
517 /// });
518 /// ```
519 fn into_with_trace<E, R, F>(self, f: F) -> Result<T, E>
520 where
521 E: Source,
522 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
523 F: FnOnce() -> R;
524}
525
526/// A type that can never be produced.
527///
528/// Never types include the unstable `!` type, enums with no variants, or any
529/// type that always contains a never type (e.g. a struct with a `Never` field).
530///
531/// # Safety
532///
533/// It must be impossible to produce a value of this type.
534pub unsafe trait Never {}
535
536/// Consumes a `Never` type, returning a primitive `!`.
537///
538/// This is a safe version of [`unreachable_unchecked`] for `Never` types.
539///
540/// # Example
541///
542/// ```
543/// use rancor::{unreachable_checked, Infallible};
544///
545/// let result = Ok::<i32, Infallible>(10);
546/// match result {
547/// Ok(i) => println!("i"),
548/// Err(e) => unreachable_checked(e),
549/// }
550/// ```
551#[inline(always)]
552pub const fn unreachable_checked<T: Never>(_: T) -> ! {
553 // SAFETY: Types that implement `Never` cannot be constructed,
554 // so this is unreachable.
555 unsafe { unreachable_unchecked() }
556}
557
558#[derive(Debug)]
559struct NoneError;
560
561impl fmt::Display for NoneError {
562 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563 write!(f, "`Option` is `None`, expected `Some`")
564 }
565}
566
567impl error::Error for NoneError {}
568
569impl<T> OptionExt<T> for Option<T> {
570 fn into_error<E>(self) -> Result<T, E>
571 where
572 E: Source,
573 {
574 match self {
575 Some(x) => Ok(x),
576 None => Err(E::new(NoneError)),
577 }
578 }
579
580 fn into_trace<E, R>(self, trace: R) -> Result<T, E>
581 where
582 E: Source,
583 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
584 {
585 match self {
586 Some(x) => Ok(x),
587 None => Err(E::new(NoneError).trace(trace)),
588 }
589 }
590
591 fn into_with_trace<E, R, F>(self, f: F) -> Result<T, E>
592 where
593 E: Source,
594 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
595 F: FnOnce() -> R,
596 {
597 match self {
598 Some(x) => Ok(x),
599 None => Err(E::new(NoneError).trace(f())),
600 }
601 }
602}
603
604/// A re-export of `core::convert::Infallible`.
605pub use core::convert::Infallible;
606
607// SAFETY: `Infallible` is an enum with no variants, and so cannot be produced.
608unsafe impl Never for Infallible {}
609
610impl Trace for Infallible {
611 fn trace<R>(self, _: R) -> Self
612 where
613 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
614 {
615 match self {}
616 }
617}
618
619/// An error type that does not occupy any space, panicking on creation instead.
620///
621/// Because panicking occurs immediately upon creation, this error type will not
622/// print any additional trace information.
623#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
624pub enum Panic {}
625
626// SAFETY: `Panic` is an enum with no variants, and so cannot be produced.
627unsafe impl Never for Panic {}
628
629impl fmt::Display for Panic {
630 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
631 match *self {}
632 }
633}
634
635impl error::Error for Panic {}
636
637impl Trace for Panic {
638 fn trace<R>(self, _: R) -> Self
639 where
640 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
641 {
642 match self {}
643 }
644}
645
646impl Source for Panic {
647 fn new<T: fmt::Display>(error: T) -> Self {
648 panic!("created a new `Panic` from: {error}");
649 }
650}
651
652/// An error type that only preserves success or failure, throwing away any more
653/// detailed error messages.
654#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
655pub struct Failure;
656
657impl fmt::Display for Failure {
658 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659 write!(f, "failed without error information")
660 }
661}
662
663impl error::Error for Failure {}
664
665impl Trace for Failure {
666 fn trace<R>(self, _: R) -> Self
667 where
668 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
669 {
670 self
671 }
672}
673
674impl Source for Failure {
675 fn new<T: error::Error + Send + Sync + 'static>(_: T) -> Self {
676 Self
677 }
678}
679
680#[cfg(feature = "alloc")]
681pub use boxed_error::BoxedError;
682
683#[cfg(all(debug_assertions, feature = "alloc"))]
684type ErrorType = BoxedError;
685#[cfg(not(all(debug_assertions, feature = "alloc")))]
686type ErrorType = Failure;
687
688/// A good general-purpose error type.
689///
690/// If `debug_assertions` and the `alloc` feature are enabled, then this error
691/// will have the same behavior as [`BoxedError`]. Otherwise, it will behave
692/// like [`Failure`].
693#[derive(Debug)]
694pub struct Error {
695 inner: ErrorType,
696}
697
698impl fmt::Display for Error {
699 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700 write!(f, "{}", self.inner)?;
701 #[cfg(not(all(debug_assertions, feature = "alloc")))]
702 write!(
703 f,
704 "; enable debug assertions and the `alloc` feature in rancor for \
705 error information"
706 )?;
707
708 Ok(())
709 }
710}
711
712impl error::Error for Error {
713 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
714 self.inner.source()
715 }
716}
717
718impl Trace for Error {
719 fn trace<R>(self, trace: R) -> Self
720 where
721 R: fmt::Debug + fmt::Display + Send + Sync + 'static,
722 {
723 Self {
724 inner: self.inner.trace(trace),
725 }
726 }
727}
728
729impl Source for Error {
730 fn new<T: error::Error + Send + Sync + 'static>(source: T) -> Self {
731 Self {
732 inner: ErrorType::new(source),
733 }
734 }
735}
736
737#[cfg(test)]
738mod test {
739 use super::*;
740
741 struct Inner {
742 value: u64,
743 }
744
745 #[test]
746 fn test_strategy() {
747 let mut inner = Inner { value: 10 };
748 let address = &inner.value as *const u64;
749 let strategy: &mut Strategy<Inner, Failure> =
750 Strategy::wrap(&mut inner);
751 let s_address = (&strategy.inner.value) as *const u64;
752 assert_eq!(address, s_address);
753
754 assert_eq!(strategy.value, 10);
755 strategy.value = 20;
756 assert_eq!(strategy.value, 20);
757 assert_eq!(inner.value, 20);
758 }
759}