Skip to content
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

[Reasoning] A valid const-generic impl Default for arrays, and eventually working to a TryFromIter implementation... #71514

Closed
ZaneHannanAU opened this issue Apr 24, 2020 · 4 comments
Labels
A-const-generics Area: const generics (parameters and arguments) C-feature-request Category: A feature request, i.e: not implemented / a PR. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@ZaneHannanAU
Copy link

This is mostly an ideas test for potentially introducing a generic impl for Default over an array of any size, rather than just up to 32.

My current implementation does not, unfortunately, implement default for arrays at zero cost; but could be useful for further checks:

use ::core::mem::MaybeUninit;
#[derive(Copy, Clone, Debug)]
struct ImplDefaultConstArray<T: Default, const N: usize> {
  arr: MaybeUninit<[T; N]>,
  idx: usize,
}
unsafe impl<T: Default, const N: usize> Drop for ImplDefaultConstArray<T, { N }> {
  fn drop(&mut self) {
    let _assigned = self.arr[..self.idx];
    // let it drop normally
  }
}
impl<T: Default, const N: usize> Default for [T; N] {
  fn default() -> Self {
    let uninit = ImplDefaultConstArray {
      arr: MaybeUninit::uninit(),
      idx: 0,
    };
    while uninit.idx < N {
      uninit.arr[uninit.idx] = T::default();
      uninit.idx += 1;
    }
    unsafe { uninit.arr.assume_init() }
  }
}

Unfortunately I believe this currently is an erroneous implementation, and has other issues regarding the real cost of it.

This could also be implemented in a less technical way if FromIterator on arrays stabilises, or rather a TryFromIterator implementation detailed below arises (note the try_collect)

use ::core::iter;
impl<T: Default, const N: usize> Default for [T; N]
  fn default() -> Self {
    #[cfg(feature = "impl_take_const")]
    iter::repeat_with(T::default)
      .take(N)
      .try_collect()
      .unwrap()
    #[cfg(not(feature = "impl_take_const"))]
    {
      let mut idx = 0;
      iter::from_fn(|| if idx < N {
        *idx += 1;
        Some(T::default())
      } else {
        None
      })
      .try_collect()
      .unwrap()
    }
  }
}

Or the original code could be a part of a generic implementation of FromIterator, or a potential trait TryFromIterator where type Error is of type TryFromSliceError, or a similarly zero-sized TryFromIteratorError; and a further is added:

impl<T: FromIterator, I> TryFromIterator<Item = I> for T {
  type Error = !;
  fn try_from_iter<Iter: Iterator<Item = I>(iter: Iter) -> Result<Self, Self::Error> {
    Ok(T::from_iter(iter))
  }
}

... I thank you for your time.

p.s: I also should really stop writing these so early... and need to adopt more official language in this circumstance. If someone can write this up as a feature request or PR more officially, that'd be amazing. Thank you.

@Elinvynia Elinvynia added A-const-generics Area: const generics (parameters and arguments) C-feature-request Category: A feature request, i.e: not implemented / a PR. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels May 21, 2020
@bstrie
Copy link
Contributor

bstrie commented Feb 1, 2021

I don't know that a hypothetical TryFromIterator has ever been discussed anywhere; it would probably be a big enough change that I would expect it to have its own RFC. It might even be overkill to have a TryFromIterator trait when instead we could impl FromIterator<T> for Option<[T; N]>; there's already precedence in the stdlib for returning None when something returning a fixed-size array gets a slice that's too small (e.g. slice::array_windows). I'll think about whipping up a PR to propose that.

@mickare
Copy link

mickare commented Jun 7, 2021

@bstrie Does not work for Result, because std has a generic implementation for FromIterator<Result> for Result.

impl<T, const N: usize> FromIterator<T> for Result<MyArray<T, N>, MyError> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        let mut iterator = iter.into_iter();
        ...
    }
}
conflicting implementations of trait `std::iter::FromIterator<std::result::Result<_, MyError>>` for type
`std::result::Result<MyArray<std::result::Result<_, MyError>, {_: usize}>, MyError>`
conflicting implementation in crate `core`:
- impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E>
  where V: FromIterator<A>;

@bstrie
Copy link
Contributor

bstrie commented Jun 7, 2021

@mickare FromIterator<T> for Result<[T; N], Foo> conflicts, but (last time I tried) FromIterator<T> for Result<[T; N], Foo<T>> does not conflict.

@dtolnay
Copy link
Member

dtolnay commented Aug 21, 2021

@dtolnay dtolnay closed this as completed Aug 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-generics Area: const generics (parameters and arguments) C-feature-request Category: A feature request, i.e: not implemented / a PR. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants