Skip to main content

Crate common_traits

Crate common_traits 

Source
Expand description

§common_traits

downloads dependents GitHub CI license Latest version Documentation

A collection of traits and types that can be used to write code that is generic over numerical types. It also provides atomic floats implemented using the integer atomic type with the same number of bits, and support for half-precision floats via the crate half.

Additionally, there are a few traits missing from the standard library, such as Sequence, variants of existing library traits such as Rng and Hash, and macros like invariant.

Finally, we provide traits for casting between types, such as UpcastableInto, and fast implementations of a few primitives such as FastRange and SelectInWord.

Everything is experimental and I’ll change them to my needs, respecting semantic versioning. :)

§Examples

Mixed precision generic dot products!

use common_traits::*;

#[inline]
pub fn dot_product<MT: Number, RT: Number, A, B>(a: A, b: B) -> RT
where
    A: Sequence,
    B: Sequence,
    A::Item: To<MT>,
    B::Item: To<MT>,
    MT: To<RT>,
    RT: To<MT>,
{
    // Ensure compatibility of the vectors
    assert_eq!(a.len(), b.len());

    // Compute the dot product
    let mut accum = RT::ZERO;
    for (a, b) in a.iter().zip(b.iter()) {
        accum = (a.to()).mul_add(b.to(), accum.to()).to();
    }

    accum
}

let x: Vec<f32> = vec![1.0, 2.0, 3.0];
let w: Vec<u8> = vec![3, 2, 1];
// compute the dot product between f32 and u8, casting to f64 and
// accumulating as u16
let res: u16 = dot_product::<f64, _, _, _>(&x, &w);
println!("{:?}", res);

§Numerical traits at a glance

The numerical traits dependency chain is the following. Black arcs represent the trait dependencies, the blue arcs represent the possibility to access an associated type implementing that trait.

§Why?

The point of making this crate public is to be able to discuss this as it covers many core missing features from Rust.

The traits in this crate are similar to the ones from num-traits but they are more interconnected (the blue arcs in the previous graph), which makes it possible to write generic code (e.g., code mixing a type and its associated atomic type) more easily and with less trait bounds.

§Summary

A highlight of common_traits’ most noteworthy features.

§Macros

This crate adds the following macros: invariant, invariant_eq, invariant_ne, which are similar to the debug_assert macros, which get checked during debug runs and get replaced with core::hint::unreachable_unchecked on release builds.

§Structs

This crate adds emulated atomic floats implemented via fetch_update for the following types:

§Numerical Traits

This crate provides the following traits for numerical types:

  • Number: something that can be added, subtracted, multiplied, divided and has a zero and a one.
  • FiniteRangeNumber: a Number which has a minimum and a maximum.
  • Float: float numbers.
  • Integer: an integer number represented as a sequence of bits.
  • SignedInt: a signed integer represented in two’s complement.
  • UnsignedInt: an unsigned integer.
§Atomic Numerical Traits

There are two main traits for working with atomic values:

  • Atomic: for values that can be read and written atomically.
  • IntoAtomic: for values that can be converted into atomic types.

Each numerical trait has an atomic equivalent:

§Miscellaneous Traits

The crate also contains a couple of extra traits:

  • Sequence, SequenceMut, and SequenceGrowable to abstract over slices and other sequence-like types.
  • AsBytes, ToBytes, and FromBytes are traits used to convert types to and from byte arrays.
  • NonZero, a version of Self that cannot be zero; UnsignedInt and SignedInt have an associated type implementing this.
  • FastRange for faster div, mod, and range operations.
  • SelectInWord to find the position of the i-th 1 or 0 in words of memory.
  • Splat to broadcast a smaller type on a larger type, mainly used for SWAR.
  • Rng for a generic random number generator.
  • SameAs, an unsafe marker trait guaranteeing that a type and its atomic variant have the same memory layout.
  • Hasher, that is like std::hash::Hasher but allows returning a generic type instead of a u64.
  • SeedableHasher, a standard way to initialize hashers.
§Conversion Traits

Traits for conversion between types are also provided:

The difference between CastableInto and To is that CastableInto does not allow casting from f32 to u32 for example, because CastableInto is implemented only between integers and between floats, while To is implemented for all primitive types.

§Features

This crate has the following features:

  • simd: to enable portable_simd and be able to do generic SIMD code
  • std: to enable standard library support
  • alloc: to enable allocator support for Vec/Box without full std
  • half: to enable support for half::f16 (experimental)
  • nightly_f16: to enable support for the native f16 type. Requires a nightly compiler and is mutually exclusive with half.

Macros§

invariant
An unsafe assert macro that checks an invariant in debug mode and optimizes it away in release mode. It has the same syntax as assert!.
invariant_eq
An assert_eq! macro to check invariants in debug mode and to optimize them away in release mode. This has the same syntax as the assert_eq! macro. See invariant! for more details.
invariant_ne
An assert_ne! macro to check invariants in debug mode and to optimize them away in release mode. This has the same syntax as the assert_ne! macro. See invariant! for more details.

Structs§

AtomicBF16
Atomic half::bf16 based on AtomicU16.
AtomicF16
Atomic half::f16 based on AtomicU16.
AtomicF32
Atomic f32 based on AtomicU32.
AtomicF64
Atomic f64 based on AtomicU64.
False
BooleanSelector version of false, this is an empty struct used only for type system bounds.
True
BooleanSelector version of true, this is an empty struct used only for type system bounds.

Traits§

AsBytes
A trait for types that have a fixed-length representation as a sequence of bytes. This includes all standard numerical scalar types.
Atomic
Values that can be atomically read and written.
AtomicFiniteRangeNumber
An atomic finite number type.
AtomicFloat
An atomic float type.
AtomicInteger
An atomic integer type.
AtomicNumber
An atomic number type.
AtomicSignedInt
An atomic signed integer type.
AtomicUnsignedInt
An atomic unsigned integer type.
BooleanSelector
Binary selection trait that makes it possible to implement traits differently on disjoint types.
CastableFrom
Trait for primitive numeric types; this is the combination of [DowncastableFrom] and [UpcastableFrom]. Prefer using the other two traits, as casting without knowing which value will be bigger might result in hard-to-find bugs.
CastableInto
CastableInto : CastableFrom = Into : From. It’s easier to use to specify bounds on generic variables.
DoubleType
A trait to access a type with double the number of bits of Self.
DowncastableFrom
Trait for primitive numeric types; the expected behavior for integers is to truncate the bits to the possibly smaller size, and for floats it will narrow to the target precision.
DowncastableInto
DowncastableInto : DowncastableFrom = Into : From. It’s easier to use to specify bounds on generic variables.
FastRange
Fast division, modulo reduction, and an alternative operation that maps a number between 0 and d.
FiniteRangeNumber
A number that has a MIN and a MAX.
Float
Common operations on floats.
FromBytes
Trait for types that can be created safely from an array of bytes.
HalfType
A trait to access a type with half the number of bits of Self.
Hash
Analog of core::hash::Hash, using Hasher.
Hasher
A generalization of core::hash::Hasher that doesn’t force the output to be u64.
Integer
A trait for operations that are shared by signed and unsigned integers.
IntoAtomic
A trait for types that have an equivalent atomic type.
IsAtomic
A trait with an associated BooleanSelector type specifying whether the type is atomic. It can be used to implement traits differently for atomic and non-atomic types. See the atomic_data example.
IsFloat
A trait with an associated BooleanSelector type specifying whether a type is a float number. It can be used to implement traits differently for float and non-float types. See the atomic_data example.
IsInteger
A trait with an associated BooleanSelector type specifying whether a type is an integer number. It can be used to implement traits differently for integer and non-integer types. See the atomic_data example.
IsNonZero
A generic trait with an associated boolean, which can be used to do specialization. See the example atomic_data for more information.
IsSigned
A trait with an associated BooleanSelector type specifying whether an integer type is signed. It can be used to implement traits differently for signed and unsigned types. See the atomic_data example.
NonZero
Non-zero variants of primitive types for enum optimizations.
Number
A trait for operations that are shared by integers and floats.
Rng
A generic random number generator.
RngNext
Implementation of a specific type generation for an Rng.
SameAs
Unsafe marker trait for types whose atomic version has the same size and bit representation.
SeedableHasher
A hasher with additional initialization parameters.
SelectInWord
Selects the rank-th 1-bit or 0-bit in a word of memory.
Sequence
A trait for types that can be viewed as a sequence of copyable elements, such as &[T].
SequenceGrowable
A trait for types that can be viewed as a growable sequence of copyable elements, such as Vec<T>.
SequenceMut
A trait for types that can be viewed as a mutable sequence of copyable elements, such as &mut [T].
SignedInt
Signed integer common operations.
Splat
Takes a smaller value and broadcasts it to all positions.
To
Casts a primitive value to another type using as.
ToBytes
Trait for types that can be cast to an array of bytes.
UnsignedInt
Unsigned integer common operations.
UpcastableFrom
Trait for primitive numeric types; the expected behavior for unsigned integers is to zero-extend the value, for signed integers it will sign-extend it to the possibly bigger size, and for floats it will widen to the target precision.
UpcastableInto
UpcastableInto : UpcastableFrom = Into : From. It’s easier to use to specify bounds on generic variables.