Skip to content

Commit

Permalink
Trait for arbitrary const values
Browse files Browse the repository at this point in the history
Introduces a trait defining an associated constant of the `Self` type.
The trait is parameterised to distinguish arbitrary constants.  An
implementation is provided for `GenericArray`, providing means of
constructing arbitrary `GenericArray`s in const context.

This plugs a gap that exists even if fizyk20#130 is merged, as generic type
parameters cannot currently be referenced in const context (e.g. if the
repetition length provided to the `arr!` macro comes from a type
parameter).
  • Loading branch information
eggyal committed Sep 11, 2022
1 parent d011b97 commit a44b8a9
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,93 @@ unsafe impl<T> ArrayLength<T> for UTerm {
type ArrayType = [T; 0];
}

/// Trait providing access to a constant value of the `Self` type; for [`GenericArray`],
/// this is an array filled with elements each having that constant value. The type parameter
/// `X` can optionally be specified to distinguish multiple different such constants if so
/// desired.
///
/// The following implementations (with default unit type for parameter `X`) are provided for
/// the primitive types:
/// * for numerics (integers and floats), value of zero
/// * for [`bool`], value of `false`
/// * for [`char`], value of the null-character `'\0'`
/// * for slices (including of [`str`]), value of the empty slice `&[]` (or empty string `""`)
/// * for raw pointers, a null pointer
/// * for tuples of up to 10 elements and arrays of any length (where element type implements
/// `ConstValue`), the tuple/array of `CONST_VALUE`s
///
/// ```rust
/// # use generic_array::{
/// # ConstValue,
/// # GenericArray,
/// # typenum::U5,
/// # };
/// use std::f32::consts::PI;
/// struct Pi;
///
/// impl ConstValue<Pi> for f32 {
/// const CONST_VALUE: Self = PI;
/// }
///
/// const ALL_ZEROS: GenericArray<usize, U5> = ConstValue::CONST_VALUE;
/// assert_eq!(ALL_ZEROS.iter().sum::<usize>(), 0);
///
/// const FIVE_PIES: GenericArray<f32, U5> = ConstValue::<Pi>::CONST_VALUE;
/// assert_eq!(FIVE_PIES[3], PI);
/// ```
pub trait ConstValue<X = ()>: Sized {
/// The constant value of the `Self` type distinguished by the type parameter `X`
const CONST_VALUE: Self;
}

macro_rules! const_value {
() => {};
(tuple: $($param:ident),*;) => {
impl<$($param: ConstValue),*> ConstValue for ($($param,)*) {
const CONST_VALUE: Self = ($($param::CONST_VALUE,)*);
}
};
(tuples:; $($rest:tt)*) => {
const_value!(tuple:;);
const_value!($($rest)*);
};
(tuples: $head:ident $(,$tail:ident)*; $($rest:tt)*) => {
const_value!(tuple: $head $(,$tail)*;);
const_value!(tuples: $($tail),*; $($rest)*);
};
($v:expr => $($t:ty),+; $($rest:tt)*) => {
$(impl ConstValue for $t {
const CONST_VALUE: Self = $v;
})+
const_value!($($rest)*);
};
}

const_value! {
0 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize;
0. => f32, f64;
false => bool;
'\0' => char;
"" => &str;
tuples: A, B, C, D, E, F, G, H, I, J;
}

impl<T: 'static> ConstValue for &[T] {
const CONST_VALUE: Self = &[];
}

impl<T: ConstValue<X>, X, const N: usize> ConstValue<X> for [T; N] {
const CONST_VALUE: Self = [T::CONST_VALUE; N];
}

impl<T> ConstValue for *const T {
const CONST_VALUE: Self = core::ptr::null();
}

impl<T> ConstValue for *mut T {
const CONST_VALUE: Self = core::ptr::null_mut();
}

/// Internal type used to generate a struct of appropriate size
#[allow(dead_code)]
#[repr(C)]
Expand All @@ -128,6 +215,14 @@ pub struct GenericArrayImplEven<T, U> {
_marker: PhantomData<T>,
}

impl<T, U: ConstValue<X>, X> ConstValue<X> for GenericArrayImplEven<T, U> {
const CONST_VALUE: Self = Self {
parent1: U::CONST_VALUE,
parent2: U::CONST_VALUE,
_marker: PhantomData,
};
}

impl<T: Clone, U: Clone> Clone for GenericArrayImplEven<T, U> {
fn clone(&self) -> GenericArrayImplEven<T, U> {
GenericArrayImplEven {
Expand All @@ -150,6 +245,14 @@ pub struct GenericArrayImplOdd<T, U> {
data: T,
}

impl<T: ConstValue<X>, U: ConstValue<X>, X> ConstValue<X> for GenericArrayImplOdd<T, U> {
const CONST_VALUE: Self = Self {
parent1: U::CONST_VALUE,
parent2: U::CONST_VALUE,
data: T::CONST_VALUE,
};
}

impl<T: Clone, U: Clone> Clone for GenericArrayImplOdd<T, U> {
fn clone(&self) -> GenericArrayImplOdd<T, U> {
GenericArrayImplOdd {
Expand Down Expand Up @@ -179,6 +282,12 @@ pub struct GenericArray<T, U: ArrayLength<T>> {
data: U::ArrayType,
}

impl<T, U: ArrayLength<T>, X> ConstValue<X> for GenericArray<T, U>
where U::ArrayType: ConstValue<X>,
{
const CONST_VALUE: Self = Self { data: U::ArrayType::CONST_VALUE };
}

unsafe impl<T: Send, N: ArrayLength<T>> Send for GenericArray<T, N> {}
unsafe impl<T: Sync, N: ArrayLength<T>> Sync for GenericArray<T, N> {}

Expand Down

0 comments on commit a44b8a9

Please sign in to comment.