-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
hybrid-array: hybrid typenum/const-generic arrays #707
Conversation
Just a little experiment I whipped up to see how well it works. I know a lot of people have been frustrated with the ergonomics of The plan there has been to migrate from A hybrid approach means we can start using const generics in user-facing APIs, but then convert those arrays to Obviously migrating away from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks interesting! I also had some minor irritations with generic-array
around interoperability with built-in arrays. But we will need to try a full-scale migration to see whether functionality in the crate is sufficient and if not, to see what has to be added.
Off the top of my head, we use sequence::Concat
in the IGE implementation (i.e. ideally we need a way to express "double/half of this block size") and it would be nice to have a stable variant of slice::as_chunks
.
Another concern is MSRV of this crate, 1.56 could be a bit too steep for some of our widely used crates (e.g. sha2
).
flex-array/src/lib.rs
Outdated
$( | ||
impl<T> Array<T, $len> for FlexArray<T, typenum::$ty> { | ||
type Length = typenum::$ty; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's worth to add an associated constant const LENGTH: usize = $len;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could do that.
Speaking of, I borrowed the ArrayLength
name from generic-array
, but within @RustCrypto it seems like we generally use *Size
instead (which I also prefer for being shorter and easier to type, if nothing else).
What about using ArraySize
instead? (and SIZE
associated constants)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, ArraySize
sounds good. Maybe we also could use Array
instead of FlexArray
and use a different name for the trait? I think the trait will be used less often than the type (though a lot of explicit GenericArays
have been replaced with type aliases based on the *User
traits). And maybe the trait is not needed in the first place, i.e. be generic over ArraySize
and have all methods be inherent on the wrapper type? UPD: Ah, scratch the last question, it probably will not work without more advanced const generics...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, Array
for the struct
type sounds good. I played around with a more minimal Array
trait before but it didn't permit the current generic impls of traits like AsRef
/Borrow
/From
, and defining all of the traits with a macro really slowed down compile times
Renamed the I'm still going to keep poking around at attempts to simplify it. Also still not wild about the name |
I would love to use this, can it get merged? Otherwise I can point to this branch as a dependency |
flex-array/src/lib.rs
Outdated
/// Flexible generic array type. | ||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] | ||
#[repr(transparent)] | ||
pub struct Array<T, U: ArrayLength<T>>(pub U::ArrayType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArrayOps
is not implemented for all Array
s, only for specific <T, length>
s because of the macro; is it possible to have as_slice
or similar methods on the struct, not just the ArrayOps
trait? This is like what generic-array does.
Since the compiler doesn't know what type has ArrayOps
implemented for it yet, it says it's not found. And all the other traits impl'd on Array
like AsRef
place a bound that requires ArrayOps
to be impl'd for Array
.
To get around this I impl'd an ArrayExt
, but maybe we can add it to the Array
struct?
pub struct Array<T, U: ArrayLength<T>>(pub U::ArrayType); | |
pub struct Array<T, U: ArrayLength<T>>(pub U::ArrayType); | |
impl<T, U> Array<T, U> | |
where | |
U: ArrayLength<T>, | |
{ | |
pub fn as_slice(&self) -> &[T] { | |
self.0.as_ref() | |
} | |
} |
This crate is a `generic-array`-inspired array type which uses some of the ideas from `crypto-bigint` to provide type-level conversions between const generics and typenum-based arrays. I originally looked at trying to add this sort of thing as an afterthought to `generic-array`, but felt that it would really be helpful if it were the core abstraction everything else is built on. Also `generic-array` is full of huge swaths of complicated `unsafe` code which is completely unnecessary in the setup used in this crate, which merely uses the type system to map to Rust's underlying array types. It provides a `FlexArray` type similar to `GenericArray` aloing with an `Array<T, const N: usize>` trait which can be used to map const generic parameters to underlying array types. Macros are used to write the `Array`/`ArrayLength` trait impls along with other traits which can't be implemented generically because of unconstrained generic parameters: `Index`/`IndexMut`, `IntoIterator`, and `TryFrom<&[T]>`. These are impl'd for the following sizes. - 0-64 - 96 - 128 - 192 - 256 - 384 - 448 - 512 - 768 - 896 - 1024 - 2048 - 8192 This is a smaller range of sizes than what `generic-array` supports due to `typenum` type alias definitions. It is necessary to make the const generic linkage work. Compile times are similar to `generic-array` with the current range of supported types. In `debug` mode I get 4s for `flex-array` and 2s for `generic-array`, and in `release` mode I get the opposite with 2s for `flex-array` and 4s for `generic-array`. More sizes could be added at the cost of more compile time. Likewise there are some self-referential relationships between the traits that could be removed to improve compile times, but seem handy. The overall API is *much* closer to `core::array` than `GenericArray` is. For example, `GenericArray` has a panicking `From` providing `TryFrom`-like behavior, and confusingly receives the blanket impl of `TryFrom`, so people can mistakenly use `TryFrom` expecting a fallible API only to get the blanket impl of `From` and a panic in the event of an error. Everywhere possible, it uses const generic implementations of traits. This includes: - `AsRef<[T; N]>` - `AsMut<[T; N]>` - `Borrow<[T; N]>` - `BorrowMut<T; N]>` - `From<[T; N]>`
The `AsRef` and `AsMut` traits still provide a trait-based equivalent
🎉🎉🎉 |
This crate is a
generic-array
-inspired array type which uses some of the ideas fromcrypto-bigint
to provide type-level conversions between const generics and typenum-based arrays.I originally looked at trying to add this sort of thing as an afterthought to
generic-array
, but felt that it would really behelpful if it were the core abstraction everything else is built on. Also
generic-array
is full of huge swaths of complicatedunsafe
code which is completely unnecessary in the setup used in this crate, which merely uses the type system to map to Rust's underlying array types. It manages to have#[forbid(unsafe_code)]
enabled, at least for now.It provides a
FlexArray
type similar toGenericArray
aloing with anArray<T, const N: usize>
trait which can be used to map const generic parameters to underlying array types.Macros are used to write the
Array
/ArrayLength
trait impls along with other traits which can't be implemented generically because of unconstrained generic parameters:Index
/IndexMut
,IntoIterator
, andTryFrom<&[T]>
. These are impl'd for the following sizes.This is a smaller range of sizes than what
generic-array
supports due totypenum
type alias definitions. It is necessary to make the const generic linkage work. (Maybeflex-array
is a bad choice of name in this regard)Compile times are currently similar to
generic-array
with the current range of supported types. Indebug
mode I get 4s forflex-array
and 2s forgeneric-array
, and inrelease
mode I get the opposite with 2s forflex-array
and 4s forgeneric-array
.More sizes could be added at the cost of more compile time. Likewise there are some self-referential relationships between the traits that could be removed to improve compile times, but seem handy.
The overall API is much closer to
core::array
thanGenericArray
is. For example,GenericArray
has a panickingFrom
providingTryFrom
-like behavior, and confusingly receives the blanket impl ofTryFrom
, so people can mistakenly useTryFrom
expecting a fallible API only to get the blanket impl ofFrom
and a panic in the event of an error.Everywhere possible, it uses const generic implementations of traits. This includes:
AsRef<[T; N]>
AsMut<[T; N]>
Borrow<[T; N]>
BorrowMut<T; N]>
From<[T; N]>
The
Array<T, const N: usize>
trait is likewise bounded on all of the impls above and can be used to express any combination of them.