multitype/lib.rs
1// Copyright 2025 Gabriel Bjørnager Jensen.
2
3#![doc(html_logo_url = "https://gitlab.com/bjoernager/multitype/-/raw/master/doc-icon.svg")]
4
5//! MultiType is a crate for generalising fundamental types via traits.
6//!
7//! MultiType provideds traits such as [`Uint`](Uint) and [`Float`](Float) traits to abstract over a set of equivalent primitive types.
8//! These traits are intended to provide one-to-one copies of the interfaces that the primitive types define.
9//!
10//! # Overview
11//!
12//! The complete list of abstraction traits is:
13//!
14//! * [`Int`]
15//! * [`IntLeast16`]
16//! * [`IntLeast32`]
17//! * [`IntLeast64`]
18//! * [`Uint`]
19//! * [`UintLeast16`]
20//! * [`UintLeast32`]
21//! * [`UintLeast64`]
22//! * [`Float`]
23//! * [`FloatLeast32`]
24#![cfg_attr(feature = "f128", doc = " * [`FloatLeast64`]")]
25#![cfg_attr(feature = "std", doc = " * [`StdFloat`]")]
26//! * [`Array`]
27//!
28//! Any given type may implement at most *one* of the aforementioned trait groups; thus, for example, any type that implemets `Float` can be assumed to possibly implement `FloatLeast32` but never `Uint`.
29//!
30//! For the sake of compatibility with <code>{[f16], [f32], [f64], [f128]}::[to_int_unchecked](f64::to_int_unchecked)</code>, we also defined our own `FloatToInt` trait.
31//!
32//! Note that all traits provided by this crate are sealed and cannot be implemented by third-party crates (at least currently).
33//!
34//! # Arithmetic types
35//!
36//! MultiType defines different traits for generalising arithmetic types:
37//!
38//! * [`Int`](Int) for [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], and [`isize`]
39//! * [`Uint`](Uint) for [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], and [`usize`]
40//! * [`Float`](Float) for [`f16`], [`f32`], [`f64`], and [`f128`]
41//!
42#![cfg_attr(feature = "std", doc = "Furthermore, `StdFloat` extends the `Float` trait with functionality that is typically only available in `std`'s floating-point types.")]
43//!
44//! ## Sized, arithmetic types
45//!
46//! The basic, arithmetic traits guarantee a minimum size that is equivalent to the smallest member of its group, e.g. `Uint` is at least `u8` and `Float` is at least `f16`.
47//!
48//! Additionally, these three arithmetic traits have subtraits that guarantee wider types, for example:
49//!
50//! * `i16` and wider implement [`IntLeast16`](IntLeast16)
51//! * `i32` and wider implement [`IntLeast32`](IntLeast32)
52//! * Etc.
53//!
54//! Extremely-wide traits, e.g. `IntLeast8` or `IntLeast128`, are considered redundant and are thus not provided.
55//!
56//! # Array types
57//!
58//! MultiType also provides the [`Array`](array::Array) trait for generalising array types -- most often over their length.
59//!
60//! An example of this trait's usecase is actually in this crate:
61//! Take, for instance, the definition of [`Uint`](Uint): It has a `Bytes` associated type that is used by the bytewise constructors and destructors:
62//!
63//! ```rust
64//! pub unsafe trait Uint: /* .. */ {
65//! type Bytes;
66//!
67//! fn from_ne_bytes(bytes: Self::Bytes) -> Self;
68//!
69//! fn to_ne_bytes(self) -> Self::Bytes;
70//!
71//! // ..
72//! }
73//! ```
74//!
75//! Now, anyone that would want to use the output of [`to_ne_bytes`](Uint::to_ne_bytes) wouldn't really have that many choices with regard to what to do with it.
76//! So, MultiType defines the `Array` trait:
77//!
78//! ```rust
79//! use multitype::Array;
80//!
81//! pub unsafe trait Uint: /* .. */ {
82//! type Bytes: Array<Scalar = u8>;
83//!
84//! // ..
85//! }
86//! ```
87//!
88//! With it, it's possible for users to generically use `Uint::to_ne_bytes` as an array type through the trait methods.
89//!
90//! # Examples
91//!
92//! A generic Fibonacci sequence:
93//!
94//! ```rust
95//! use multitype::Uint;
96//!
97//! fn f<T: Uint>(x: T) -> T {
98//! let mut y = T::from(0x0u8);
99//! let mut y_m1 = T::from(0x0u8);
100//! let mut y_m2 = T::from(0x1u8);
101//!
102#![cfg_attr( feature = "step", doc = " for i in T::from(0x0u8)..x {\n")]
103#![cfg_attr(not(feature = "step"), doc = " let mut i = T::from(0x0u8);\n")]
104#![cfg_attr(not(feature = "step"), doc = " while i < x {\n")]
105//! y = y_m1 + y_m2;
106//!
107//! y_m2 = y_m1;
108//! y_m1 = y;
109#![cfg_attr(not(feature = "step"), doc = "\n")]
110#![cfg_attr(not(feature = "step"), doc = " i += T::from(0x1u8);\n")]
111//! }
112//!
113//! y
114//! }
115//!
116//! assert_eq!(f(0u8), 0);
117//! assert_eq!(f(1u8), 1);
118//!
119//! assert_eq!(f(2u16), 1);
120//! assert_eq!(f(3u16), 2);
121//!
122//! assert_eq!(f(4u32), 3);
123//! assert_eq!(f(5u32), 5);
124//!
125//! assert_eq!(f(6u64), 8);
126//! assert_eq!(f(7u64), 13);
127//! ```
128//!
129//! Generic array indexing:
130//!
131//! ```rust
132//! use core::f32;
133//! use multitype::{Array, Float};
134//!
135//! fn complicated_neg<T: Float>(value: T) -> T {
136//! let mut bytes = value.to_le_bytes();
137//!
138//! // Invert the sign bit -- which is always the most
139//! // significant bit of the most significant byte.
140//! *bytes.as_mut_slice().last_mut().unwrap() ^= 0b10000000;
141//!
142//! T::from_le_bytes(bytes)
143//! }
144//!
145//! assert_eq!(complicated_neg( 1.0f64), -1.0f64);
146//! assert_eq!(complicated_neg(-1.0f64), 1.0f64);
147//!
148//! assert_eq!(complicated_neg(f32::NEG_INFINITY), f32::INFINITY);
149//! ```
150//!
151//! # Feature flags
152//!
153//! Default features:
154//! * `alloc`
155//! * `std`
156//!
157//! Dependency features:
158#![cfg_attr(feature = "alloc", doc = "* `alloc`: Enables compatibility with [`alloc`] facilities\n")]
159#![cfg_attr(not(feature = "alloc"), doc = "* `alloc`: Enables compatibility with `alloc` facilities\n")]
160#![cfg_attr(feature = "std", doc = "* `std`: Enables compatibility with [`std`] facilities\n")]
161#![cfg_attr(not(feature = "std"), doc = "* `std`: Enables compatibility with `std` facilities\n")]
162//!
163//! Unstable features:
164//! * `clone_to_uninit`: Enables [`CloneToUninit`](core::clone::CloneToUninit) requirements
165//! * `const_param_ty`: Enables [`ConstParamTy_`](core::marker::ConstParamTy_) requirements
166//! * `f128`: Enables support for [`f128`]
167//! * `f16`: Enables support for [`f16`]
168//! * `freeze`: Enables [`Freeze`](core::marker::Freeze) requirements
169//! * `step`: Enables [`Step`](core::iter::Step) requirements
170//! * `structural_partial_eq`: Enables [`StructuralPartialEq`](core::marker::StructuralPartialEq) requirements
171//! * `thin`: Enables [`Thin`](core::ptr::Thin) requirements
172//! * `trusted_step`: Enables [`TrustedStep`](core::iter::TrustedStep) requirements
173//! * `unstable-docs`: Enables unstable documentation features requirements
174//! * `use_cloned`: Enables [`UseCloned`](core::clone::UseCloned) requirements
175//!
176//! Unstable features can be expected to be removed as their facilities stabilise.
177//!
178//! # MSRV policy
179//!
180//! The goal of MultiType is to provide generic traits that bind as much of the standard interfaces as possible.
181//! We will attempt to backport all trivial interfaces as much as possible, but if any given interface is deemed to complicated, we will bump the MSRV to leverage it from the standard implementation.
182//!
183//! When `const`-compatible traits land, MultiType will implement the feature as quickly as possible.
184//!
185//! # Copyright & Licence.
186//!
187//! Copyright © 2025 Gabriel Bjørnager Jensen.
188//!
189//! MultiType is distributed under either an MIT licence (see `LICENCE-MIT`) or version 2.0 of the Apache License (see `LICENCE-APACHE`), at your option.
190
191#![no_std]
192
193#![cfg_attr(feature = "use_cloned", expect(incomplete_features))]
194
195#![cfg_attr(feature = "clone_to_uninit", feature(clone_to_uninit))]
196#![cfg_attr(feature = "const_param_ty", feature(unsized_const_params))]
197#![cfg_attr(feature = "f16", feature(f16))]
198#![cfg_attr(feature = "f128", feature(f128))]
199#![cfg_attr(feature = "freeze", feature(freeze))]
200#![cfg_attr(feature = "step", feature(step_trait))]
201#![cfg_attr(feature = "structural_partial_eq", feature(structural_match))]
202#![cfg_attr(feature = "thin", feature(ptr_metadata))]
203#![cfg_attr(feature = "trusted_step", feature(trusted_step))]
204#![cfg_attr(feature = "unstable-docs", feature(doc_cfg, intra_doc_pointers))]
205#![cfg_attr(feature = "use_cloned", feature(ergonomic_clones))]
206
207extern crate self as multitype;
208
209#[cfg(feature = "alloc")]
210extern crate alloc;
211
212#[cfg(feature = "std")]
213extern crate std;
214
215mod array;
216mod compat;
217mod float;
218mod float_to_int;
219mod float_least32;
220mod float_least64;
221mod int;
222mod int_least16;
223mod int_least32;
224mod int_least64;
225mod std_float;
226mod uint;
227mod uint_least16;
228mod uint_least32;
229mod uint_least64;
230
231pub use array::Array;
232pub use float::Float;
233pub use float_least32::FloatLeast32;
234pub use int::Int;
235pub use int_least16::IntLeast16;
236pub use int_least32::IntLeast32;
237pub use int_least64::IntLeast64;
238pub use uint::Uint;
239pub use uint_least16::UintLeast16;
240pub use uint_least32::UintLeast32;
241pub use uint_least64::UintLeast64;
242
243#[cfg(feature = "f128")]
244pub use float_least64::FloatLeast64;
245
246#[cfg(feature = "std")]
247pub use std_float::StdFloat;
248
249use float_to_int::FloatToInt;
250
251mod seal {
252 pub trait Uint {}
253 pub trait UintLeast16 {}
254 pub trait UintLeast32 {}
255 pub trait UintLeast64 {}
256
257 pub trait Int {}
258 pub trait IntLeast16 {}
259 pub trait IntLeast32 {}
260 pub trait IntLeast64 {}
261
262 pub trait Float {}
263 pub trait FloatLeast32 {}
264
265 pub trait Array {}
266
267 #[cfg(feature = "f128")]
268 pub trait FloatLeast64 {}
269
270 #[cfg(feature = "std")]
271 pub trait StdFloat {}
272}