Skip to content
This repository has been archived by the owner on Sep 11, 2023. It is now read-only.

Commit

Permalink
Use Generic Associated Types for Enum trait
Browse files Browse the repository at this point in the history
  • Loading branch information
KamilaBorowska committed Sep 7, 2023
1 parent 1959870 commit c39739e
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 139 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
toolchain:
- 1.61
- "1.65"
- stable
- beta
- nightly
Expand Down
7 changes: 1 addition & 6 deletions enum-map-derive/src/derive_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl EnumGenerator {
quote! {
#[automatically_derived]
impl ::enum_map::Enum for #name {
const LENGTH: ::enum_map::usize = #length;
type Array<V> = [V; #length];

#[inline]
fn from_usize(value: ::enum_map::usize) -> Self {
Expand All @@ -53,11 +53,6 @@ impl EnumGenerator {
}
}
}

#[automatically_derived]
impl<V> ::enum_map::EnumArray<V> for #name {
type Array = [V; #length];
}
}
}

Expand Down
7 changes: 1 addition & 6 deletions enum-map-derive/src/derive_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl StructGenerator {
quote! {
#[automatically_derived]
impl ::enum_map::Enum for #name {
const LENGTH: ::enum_map::usize = #length;
type Array<V> = [V; #length];

#[inline]
fn from_usize(value: ::enum_map::usize) -> Self {
Expand All @@ -114,11 +114,6 @@ impl StructGenerator {
#into_usize
}
}

#[automatically_derived]
impl<V> ::enum_map::EnumArray<V> for #name {
type Array = [V; #length];
}
}
}
}
2 changes: 1 addition & 1 deletion enum-map-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,6 @@ pub fn derive_enum_map(input: proc_macro::TokenStream) -> proc_macro::TokenStrea

fn type_length(ty: &Type) -> TokenStream {
quote! {
<#ty as ::enum_map::Enum>::LENGTH
::enum_map::enum_len::<#ty>()
}
}
2 changes: 1 addition & 1 deletion enum-map/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "enum-map"
version = "2.6.2"
authors = ["Konrad Borowski <konrad@borowski.pw>"]
edition = "2021"
rust-version = "1.61"
rust-version = "1.65"
repository = "https://github.com/xfix/enum-map"
license = "MIT OR Apache-2.0"
description = "A map with C-like enum keys represented internally as an array"
Expand Down
12 changes: 7 additions & 5 deletions enum-map/src/arbitrary.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
use crate::{enum_map, EnumArray, EnumMap};
use crate::internal::Array;
use crate::{enum_map, Enum, EnumMap};
use arbitrary::{Arbitrary, Result, Unstructured};

/// Requires crate feature `"arbitrary"`
impl<'a, K: EnumArray<V>, V: Arbitrary<'a>> Arbitrary<'a> for EnumMap<K, V> {
impl<'a, K: Enum, V: Arbitrary<'a>> Arbitrary<'a> for EnumMap<K, V> {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<EnumMap<K, V>> {
Ok(enum_map! {
_ => Arbitrary::arbitrary(u)?,
})
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
if K::LENGTH == 0 {
let len = K::Array::<V>::LENGTH;
if len == 0 {
(0, Some(0))
} else {
let (lo, hi) = V::size_hint(depth);
(
lo.saturating_mul(K::LENGTH),
hi.and_then(|hi| hi.checked_mul(K::LENGTH)),
lo.saturating_mul(len),
hi.and_then(|hi| hi.checked_mul(len)),
)
}
}
Expand Down
32 changes: 16 additions & 16 deletions enum-map/src/enum_map_impls.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use crate::{enum_map, EnumArray, EnumMap};
use crate::{enum_map, Enum, EnumMap};
use core::fmt::{self, Debug, Formatter};
use core::hash::{Hash, Hasher};
use core::iter::{Extend, FromIterator};
use core::ops::{Index, IndexMut};

impl<K: EnumArray<V> + Debug, V: Debug> Debug for EnumMap<K, V> {
impl<K: Enum + Debug, V: Debug> Debug for EnumMap<K, V> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_map().entries(self).finish()
}
}

impl<K: EnumArray<V>, V> Extend<(K, V)> for EnumMap<K, V> {
impl<K: Enum, V> Extend<(K, V)> for EnumMap<K, V> {
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
for (key, value) in iter {
self[key] = value;
Expand All @@ -20,7 +20,7 @@ impl<K: EnumArray<V>, V> Extend<(K, V)> for EnumMap<K, V> {

impl<'a, K, V> Extend<(&'a K, &'a V)> for EnumMap<K, V>
where
K: EnumArray<V> + Copy,
K: Enum + Copy,
V: Copy,
{
fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
Expand All @@ -31,7 +31,7 @@ where
impl<K, V> FromIterator<(K, V)> for EnumMap<K, V>
where
Self: Default,
K: EnumArray<V>,
K: Enum,
{
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let mut map = EnumMap::default();
Expand All @@ -40,7 +40,7 @@ where
}
}

impl<K: EnumArray<V>, V> Index<K> for EnumMap<K, V> {
impl<K: Enum, V> Index<K> for EnumMap<K, V> {
type Output = V;

#[inline]
Expand All @@ -49,7 +49,7 @@ impl<K: EnumArray<V>, V> Index<K> for EnumMap<K, V> {
}
}

impl<K: EnumArray<V>, V> IndexMut<K> for EnumMap<K, V> {
impl<K: Enum, V> IndexMut<K> for EnumMap<K, V> {
#[inline]
fn index_mut(&mut self, key: K) -> &mut V {
&mut self.as_mut_slice()[key.into_usize()]
Expand All @@ -58,9 +58,9 @@ impl<K: EnumArray<V>, V> IndexMut<K> for EnumMap<K, V> {

// Implementations provided by derive attribute are too specific, and put requirements on K.
// This is caused by rust-lang/rust#26925.
impl<K: EnumArray<V>, V> Clone for EnumMap<K, V>
impl<K: Enum, V> Clone for EnumMap<K, V>
where
K::Array: Clone,
K::Array<V>: Clone,
{
#[inline]
fn clone(&self) -> Self {
Expand All @@ -70,38 +70,38 @@ where
}
}

impl<K: EnumArray<V>, V> Copy for EnumMap<K, V> where K::Array: Copy {}
impl<K: Enum, V> Copy for EnumMap<K, V> where K::Array<V>: Copy {}

impl<K: EnumArray<V>, V: PartialEq> PartialEq for EnumMap<K, V> {
impl<K: Enum, V: PartialEq> PartialEq for EnumMap<K, V> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}

impl<K: EnumArray<V>, V: Eq> Eq for EnumMap<K, V> {}
impl<K: Enum, V: Eq> Eq for EnumMap<K, V> {}

impl<K: EnumArray<V>, V: Hash> Hash for EnumMap<K, V> {
impl<K: Enum, V: Hash> Hash for EnumMap<K, V> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_slice().hash(state);
}
}

impl<K: EnumArray<V>, V: Default> Default for EnumMap<K, V> {
impl<K: Enum, V: Default> Default for EnumMap<K, V> {
#[inline]
fn default() -> Self {
enum_map! { _ => V::default() }
}
}

impl<K: EnumArray<V>, V: PartialOrd> PartialOrd for EnumMap<K, V> {
impl<K: Enum, V: PartialOrd> PartialOrd for EnumMap<K, V> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.as_slice().partial_cmp(other.as_slice())
}
}

impl<K: EnumArray<V>, V: Ord> Ord for EnumMap<K, V> {
impl<K: Enum, V: Ord> Ord for EnumMap<K, V> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_slice().cmp(other.as_slice())
}
Expand Down
54 changes: 15 additions & 39 deletions enum-map/src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,35 @@ use core::convert::Infallible;
/// strictly speaking not an actual enum, there are good reasons to consider
/// it like one, as array of `u8` keys is a relatively common pattern.
pub trait Enum: Sized {
/// Length of the enum.
const LENGTH: usize;
/// Representation of an enum.
///
/// For an enum with four elements it looks like this.
///
/// ```
/// type Array<V> = [V; 4];
/// ```
type Array<V>: Array;

/// Takes an usize, and returns an element matching `into_usize` function.
fn from_usize(value: usize) -> Self;
/// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
fn into_usize(self) -> usize;
}

/// Trait associating enum with an array.
///
/// This exists due to limitations of Rust compiler that prevent arrays from using
/// associated constants in structures. The array length must match `LENGTH` of an
/// `Enum`.
pub trait EnumArray<V>: Enum {
/// Representation of an enum map for type `V`.
type Array: Array<V>;
}

/// Array for enum-map storage.
///
/// This trait is inteded for primitive array types (with fixed length).
///
/// # Safety
///
/// The array length needs to match actual storage.
pub unsafe trait Array<V> {
pub unsafe trait Array {
// This is necessary duplication because the length in Enum trait can be
// provided by user and may not be trustworthy for unsafe code.
const LENGTH: usize;
}

unsafe impl<V, const N: usize> Array<V> for [V; N] {
unsafe impl<V, const N: usize> Array for [V; N] {
const LENGTH: usize = N;
}

Expand All @@ -52,7 +48,7 @@ pub fn out_of_bounds() -> ! {
}

impl Enum for bool {
const LENGTH: usize = 2;
type Array<V> = [V; 2];

#[inline]
fn from_usize(value: usize) -> Self {
Expand All @@ -68,12 +64,8 @@ impl Enum for bool {
}
}

impl<T> EnumArray<T> for bool {
type Array = [T; Self::LENGTH];
}

impl Enum for () {
const LENGTH: usize = 1;
type Array<V> = [V; 1];

#[inline]
fn from_usize(value: usize) -> Self {
Expand All @@ -88,12 +80,8 @@ impl Enum for () {
}
}

impl<T> EnumArray<T> for () {
type Array = [T; Self::LENGTH];
}

impl Enum for u8 {
const LENGTH: usize = 256;
type Array<V> = [V; 256];

#[inline]
fn from_usize(value: usize) -> Self {
Expand All @@ -105,12 +93,8 @@ impl Enum for u8 {
}
}

impl<T> EnumArray<T> for u8 {
type Array = [T; Self::LENGTH];
}

impl Enum for Infallible {
const LENGTH: usize = 0;
type Array<V> = [V; 0];

#[inline]
fn from_usize(_: usize) -> Self {
Expand All @@ -122,12 +106,8 @@ impl Enum for Infallible {
}
}

impl<T> EnumArray<T> for Infallible {
type Array = [T; Self::LENGTH];
}

impl Enum for Ordering {
const LENGTH: usize = 3;
type Array<V> = [V; 3];

#[inline]
fn from_usize(value: usize) -> Self {
Expand All @@ -147,7 +127,3 @@ impl Enum for Ordering {
}
}
}

impl<T> EnumArray<T> for Ordering {
type Array = [T; Self::LENGTH];
}

0 comments on commit c39739e

Please sign in to comment.