-
Notifications
You must be signed in to change notification settings - Fork 130
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
Support for constant generics #125
Comments
Yes! Const generics is "on the roadmap" as the item for ArrayVec 2.0. See #48 |
Hi @bluss Glad to know your presence. I opened a PR that adds support for constant generics in |
@c410-f3r Sure. Unfortunately I will probably not be able to look at it before September. Maybe it's best to at least allow the 0.5/master changes to rip out nodrop and go all in on MaybeUninit, land first, too. |
The best approach I could think of, to avoid code duplication and keep compatibility with the legacy #![cfg_attr(feature = "const-generics", feature(const_generics))]
use core::mem::MaybeUninit;
macro_rules! create_with_parts {
(
<$($({$s_impl_ty_prefix:ident})? $s_impl_ty:ident$(: $s_impl_ty_bound:ident)?),*>,
<$s_decl_ty:ident$(, {$s_decl_const_ty:ident})?>,
$array:ty
) => {
pub struct ArrayVec<$($($s_impl_ty_prefix)? $s_impl_ty$(: $s_impl_ty_bound)?),*>
{
xs: MaybeUninit<$array>,
len: usize
}
impl<$($($s_impl_ty_prefix)? $s_impl_ty$(: $s_impl_ty_bound)?),*>
ArrayVec<$s_decl_ty$(, {$s_decl_const_ty})?>
{
}
}
}
#[cfg(feature = "const-generics")]
create_with_parts!(<T, {const} N: usize>, <T, {N}>, [T; N]);
#[cfg(not(feature = "const-generics"))]
create_with_parts!(<A: Array>, <A>, A); Resolves into: pub struct ArrayVec<A: Array>
{
xs: MaybeUninit<A>,
len: usize
}
impl<A: Array> ArrayVec<A> {} or pub struct ArrayVec<T, const N: usize>
{
xs: MaybeUninit<[T; N]>,
len: usize
}
impl<T, const N: usize> ArrayVec<T, {N}> {} It is a bit verbose but it works. Could there be any other way? However, as you may have noticed, #![feature(const_generics)]
use core::mem::MaybeUninit;
macro_rules! create_with_parts {
(
<$($({$s_impl_ty_prefix:ident})? $s_impl_ty:ident$(: $s_impl_ty_bound:ident)?),*>,
<$s_decl_ty:ident$(, {$s_decl_const_ty:ident})?>,
$array:ty
) => {
pub struct ArrayVec<I: Index, $($($s_impl_ty_prefix)? $s_impl_ty$(: $s_impl_ty_bound)?),*>
{
xs: MaybeUninit<$array>,
len: I
}
impl<I: Index, $($($s_impl_ty_prefix)? $s_impl_ty$(: $s_impl_ty_bound)?),*>
ArrayVec<I, $s_decl_ty$(, {$s_decl_const_ty})?>
{
}
}
}
#[cfg(feature = "const-generics")]
create_with_parts!(<T, {const} N: usize>, <T, {N}>, [T; N]);
#[cfg(not(feature = "const-generics"))]
create_with_parts!(<A: Array>, <A>, A); Resolves into: pub struct ArrayVec<I: Index, A: Array>
{
xs: MaybeUninit<A>,
len: I
}
impl<I: Index, A: Array> ArrayVec<I, A> {} or pub struct ArrayVec<I: Index, T, const N: usize>
{
xs: MaybeUninit<[T; N]>,
len: I
}
impl<I: Index, T, const N: usize> ArrayVec<I, T, {N}> {} Please, let me know what you think about this whole process. |
I don't have a plan for how to handle const generics before it's stable. Looks like you have a cool plan there, that could work, but it doesn't seem appropriate for a crate feature - the whole API changes when the feature is enabled. It looks more like it has to be a fork or a separate version. On the topic of indices, that is an expected loss. I'd say we don't want an index parameter. Either the index type should depend on N (if possible), or it simply will have to be a "hardcoded" index type. (Maybe have several if we can type alias the Main variants?) |
I see. Stabilization of |
What I said of making the whole API change might be premature - there could be ways with type aliases and other ways to completely mask the const generics? With that said, once we go all out on stable const generics, we don't need the old Array trait, of course, and we should remove it and forget it -- make the best API we can with const generics. |
Well, it is possible to implement unsafe trait Array {
type Item;
type Index;
const CAPACITY: usize;
fn as_slice(&self) -> &[Self::Item];
fn as_mut_slice(&mut self) -> &mut [Self::Item];
}
#[cfg(feature = "const-generics")]
unsafe impl<T, const N: usize> Array for [T; N] {
const CAPACITY: usize = N;
type Item = T;
type Index = usize; // Hardcoded
fn as_slice(&self) -> &[Self::Item] {
self
}
fn as_mut_slice(&mut self) -> &mut [Self::Item] {
self
}
}
#[cfg(not(feature = "const-generics"))]
// Legacy implementation The problem with this approach is that #96 will still remain (maybe remove |
Potentially bad idea: why not make |
Sounds good, will work well on x86 at least I guess. For other platforms, there might be alignment issues, e.g. if you have a 256 byte array of bytes, it requires a 16 bit integer as index, but that will potentially not be aligned, leading to more expensive stores/loads. |
That's actually a good point; I wonder if there would be an easy way around that besides forcing alignment. |
Just to say that it's possible to define a quick Array trait wrapper from outside the crate (using arrayvec from master). Just in this code, I found ICEs in most of the things I wanted to do, so it doesn't seem to be exactly ready 🙂 #![feature(const_generics)]
use arrayvec::{Array, ArrayVec};
#[repr(transparent)]
struct Data<T, const N: usize>(pub [T; N]);
unsafe impl<T, const N: usize> Array for Data<T, {N}> {
const CAPACITY: usize = N;
type Index = usize;
type Item = T;
fn as_slice(&self) -> &[Self::Item] { &self.0 }
fn as_mut_slice(&mut self) -> &mut [Self::Item] { &mut self.0 }
}
/* compiler error / ICE
struct BTreeNode<const B: usize> {
keys: ArrayVec<Data<i32, {2 * B}>>,
children: ArrayVec<Data<i32, {2 * B + 1}>>,
}
*/
// ICEs on use
//type CArrayVec<T, const N: usize> = ArrayVec<Data<T, {N}>>;
fn main() {
//let mut v = CArrayVec::from(Data([0; 47]));
let mut u = ArrayVec::from(Data([0; 47]));
let mut i = 0;
for elt in &mut u {
*elt = i;
i += 1;
}
println!("{:?}", u);
} (using rustc 1.39.0-nightly (9b9d2aff8 2019-09-19) ) |
I just did a write-up on implementing pub struct ArrayVec<T, const N: usize> {
items: [MaybeUninit<T>; N],
length: usize,
} Throughout the entire process I didn't hit a single ICE, so maybe it's worth giving another shot? |
@Michael-F-Bryan Yes, I think we should, if it makes sense. There might be other crates already filling this need? |
@bluss I think a better intermediary solution could be to add a |
Perhaps the best approach would be to have an unstable 2.0 branch that could be used by folks who want const generics but is kept in an alpha state until those features are merged. A feature isn't the best as the API should change for const generics IMHO. |
Constant generics is becoming more mature and soon will be in an usable state for complex projects.
The text was updated successfully, but these errors were encountered: