From eddf2f8c684576be2314027706cfdd344c412327 Mon Sep 17 00:00:00 2001 From: bendn Date: Fri, 26 Sep 2025 22:51:53 +0700 Subject: [PATCH 1/3] tests --- library/coretests/tests/array.rs | 14 ++++++++++++++ library/coretests/tests/lib.rs | 2 ++ 2 files changed, 16 insertions(+) diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index c4a8fc74feca3..bdea7d4c0bdb1 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -724,3 +724,17 @@ fn array_eq() { let not_true = [0u8] == [].as_slice(); assert!(!not_true); } + +#[test] +fn const_array_ops() { + const fn doubler(x: usize) -> usize { + x * 2 + } + const fn maybe_doubler(x: usize) -> Option { + x.checked_mul(2) + } + assert_eq!(const { std::array::from_fn::<_, 5, _>(doubler) }, [0, 2, 4, 6, 8]); + assert_eq!(const { [5, 6, 1, 2].map(doubler) }, [10, 12, 2, 4]); + assert_eq!(const { [1, usize::MAX, 2, 8].try_map(maybe_doubler) }, None); + assert_eq!(const { std::array::try_from_fn::<_, 5, _>(maybe_doubler) }, Some([0, 2, 4, 6, 8])); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 48982d71e8137..7b0b609f0af64 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -4,6 +4,7 @@ #![feature(alloc_layout_extra)] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] +#![feature(array_try_map)] #![feature(array_windows)] #![feature(ascii_char)] #![feature(ascii_char_variants)] @@ -17,6 +18,7 @@ #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(const_cell_traits)] +#![feature(const_array)] #![feature(const_cmp)] #![feature(const_convert)] #![feature(const_destruct)] From 1d718e20ace4fdb554550049b65034c9fe1fd23d Mon Sep 17 00:00:00 2001 From: bendn Date: Sun, 12 Oct 2025 17:39:28 +0700 Subject: [PATCH 2/3] constify from_fn, try_from_fn, try_map, map --- library/core/src/array/drain.rs | 128 +++++++++++++++--------------- library/core/src/array/mod.rs | 61 +++++++++----- library/core/src/ops/try_trait.rs | 40 +++++++--- library/coretests/tests/lib.rs | 2 +- 4 files changed, 135 insertions(+), 96 deletions(-) diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 5fadf907b6219..6ab649c036b43 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -1,76 +1,76 @@ -use crate::iter::{TrustedLen, UncheckedIterator}; +use crate::assert_unsafe_precondition; +use crate::marker::Destruct; use crate::mem::ManuallyDrop; -use crate::ptr::drop_in_place; -use crate::slice; -/// A situationally-optimized version of `array.into_iter().for_each(func)`. -/// -/// [`crate::array::IntoIter`]s are great when you need an owned iterator, but -/// storing the entire array *inside* the iterator like that can sometimes -/// pessimize code. Notable, it can be more bytes than you really want to move -/// around, and because the array accesses index into it SRoA has a harder time -/// optimizing away the type than it does iterators that just hold a couple pointers. -/// -/// Thus this function exists, which gives a way to get *moved* access to the -/// elements of an array using a small iterator -- no bigger than a slice iterator. -/// -/// The function-taking-a-closure structure makes it safe, as it keeps callers -/// from looking at already-dropped elements. -pub(crate) fn drain_array_with( - array: [T; N], - func: impl for<'a> FnOnce(Drain<'a, T>) -> R, -) -> R { - let mut array = ManuallyDrop::new(array); - // SAFETY: Now that the local won't drop it, it's ok to construct the `Drain` which will. - let drain = Drain(array.iter_mut()); - func(drain) +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +pub(super) struct Drain<'a, T, U, const N: usize, F: FnMut(T) -> U> { + array: ManuallyDrop<[T; N]>, + moved: usize, + f: &'a mut F, } +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const FnOnce<(usize,)> for &mut Drain<'_, T, U, N, F> +where + F: [const] FnMut(T) -> U, +{ + type Output = U; -/// See [`drain_array_with`] -- this is `pub(crate)` only so it's allowed to be -/// mentioned in the signature of that method. (Otherwise it hits `E0446`.) -// INVARIANT: It's ok to drop the remainder of the inner iterator. -pub(crate) struct Drain<'a, T>(slice::IterMut<'a, T>); - -impl Drop for Drain<'_, T> { - fn drop(&mut self) { - // SAFETY: By the type invariant, we're allowed to drop all these. - unsafe { drop_in_place(self.0.as_mut_slice()) } + extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { + self.call_mut(args) } } - -impl Iterator for Drain<'_, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - let p: *const T = self.0.next()?; - // SAFETY: The iterator was already advanced, so we won't drop this later. - Some(unsafe { p.read() }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.len(); - (n, Some(n)) +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const FnMut<(usize,)> for &mut Drain<'_, T, U, N, F> +where + F: [const] FnMut(T) -> U, +{ + extern "rust-call" fn call_mut(&mut self, (i,): (usize,)) -> Self::Output { + // SAFETY: increment moved before moving. if `f` panics, we drop the rest. + self.moved += 1; + assert_unsafe_precondition!( + check_library_ub, + "musnt index array out of bounds", (i: usize = i, size: usize = N) => i < size + ); + // SAFETY: the `i` should also always go up, and musnt skip any, else some things will be leaked. + // SAFETY: if it goes down, we will drop freed elements. not good. + // SAFETY: caller guarantees never called with number >= N (see `Drain::new`) + (self.f)(unsafe { self.array.as_ptr().add(i).read() }) } } - -impl ExactSizeIterator for Drain<'_, T> { - #[inline] - fn len(&self) -> usize { - self.0.len() +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl U> const Drop + for Drain<'_, T, U, N, F> +{ + fn drop(&mut self) { + let mut n = self.moved; + while n != N { + // SAFETY: moved must always be < N + unsafe { self.array.as_mut_ptr().add(n).drop_in_place() }; + n += 1; + } } } - -// SAFETY: This is a 1:1 wrapper for a slice iterator, which is also `TrustedLen`. -unsafe impl TrustedLen for Drain<'_, T> {} - -impl UncheckedIterator for Drain<'_, T> { - unsafe fn next_unchecked(&mut self) -> T { - // SAFETY: `Drain` is 1:1 with the inner iterator, so if the caller promised - // that there's an element left, the inner iterator has one too. - let p: *const T = unsafe { self.0.next_unchecked() }; - // SAFETY: The iterator was already advanced, so we won't drop this later. - unsafe { p.read() } +impl<'a, T, U, const N: usize, F: FnMut(T) -> U> Drain<'a, T, U, N, F> { + /// This function returns a function that lets you index the given array in const. + /// As implemented it can optimize better than iterators, and can be constified. + /// It acts like a sort of guard and iterator combined, which can be implemented + /// as it is a struct that implements const fn; + /// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`. + /// The only method you're really allowed to call is `next()`, + /// anything else is more or less UB, hence this function being unsafe. + /// Moved elements will not be dropped. + /// + /// Previously this was implemented as a wrapper around a `slice::Iter`, which + /// called `read()` on the returned `&T`; gnarly stuff. + /// + /// SAFETY: must be called in order of 0..N, without indexing out of bounds. (see `Drain::call_mut`) + /// Potentially the function could completely disregard the supplied argument, however i think that behaviour would be unintuitive. + // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. + pub(super) const unsafe fn new(array: [T; N], f: &'a mut F) -> Self { + Self { array: ManuallyDrop::new(array), moved: 0, f } } } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 2dd639d68f0ea..bbf75c70d6963 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -12,6 +12,7 @@ use crate::error::Error; use crate::hash::{self, Hash}; use crate::intrinsics::transmute_unchecked; use crate::iter::{UncheckedIterator, repeat_n}; +use crate::marker::Destruct; use crate::mem::{self, MaybeUninit}; use crate::ops::{ ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try, @@ -25,7 +26,6 @@ mod drain; mod equality; mod iter; -pub(crate) use drain::drain_array_with; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; @@ -105,9 +105,10 @@ pub fn repeat(val: T) -> [T; N] { /// ``` #[inline] #[stable(feature = "array_from_fn", since = "1.63.0")] -pub fn from_fn(f: F) -> [T; N] +#[rustc_const_unstable(feature = "const_array", issue = "147606")] +pub const fn from_fn(f: F) -> [T; N] where - F: FnMut(usize) -> T, + F: [const] FnMut(usize) -> T + [const] Destruct, { try_from_fn(NeverShortCircuit::wrap_mut_1(f)).0 } @@ -143,11 +144,15 @@ where /// ``` #[inline] #[unstable(feature = "array_try_from_fn", issue = "89379")] -pub fn try_from_fn(cb: F) -> ChangeOutputType +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +pub const fn try_from_fn( + cb: impl [const] FnMut(usize) -> R + [const] Destruct, +) -> ChangeOutputType where - F: FnMut(usize) -> R, - R: Try, - R::Residual: Residual<[R::Output; N]>, + R: [const] Try< + Residual: [const] Residual<[R::Output; N], TryType: [const] Try>, + Output: [const] Destruct, + >, { let mut array = [const { MaybeUninit::uninit() }; N]; match try_from_fn_erased(&mut array, cb) { @@ -549,9 +554,11 @@ impl [T; N] { /// ``` #[must_use] #[stable(feature = "array_map", since = "1.55.0")] - pub fn map(self, f: F) -> [U; N] + #[rustc_const_unstable(feature = "const_array", issue = "147606")] + pub const fn map(self, f: F) -> [U; N] where - F: FnMut(T) -> U, + F: [const] FnMut(T) -> U + [const] Destruct, + U: [const] Destruct, { self.try_map(NeverShortCircuit::wrap_mut_1(f)).0 } @@ -587,11 +594,22 @@ impl [T; N] { /// assert_eq!(c, Some(a)); /// ``` #[unstable(feature = "array_try_map", issue = "79711")] - pub fn try_map(self, f: impl FnMut(T) -> R) -> ChangeOutputType + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] + pub const fn try_map( + self, + mut f: impl [const] FnMut(T) -> R + [const] Destruct, + ) -> ChangeOutputType where - R: Try>, + R: [const] Try< + Residual: [const] Residual<[R::Output; N], TryType: [const] Try>, + Output: [const] Destruct, + >, { - drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f))) + // SAFETY: try_from_fn calls `f` with 0..N. + let mut f = unsafe { drain::Drain::new(self, &mut f) }; + let out = try_from_fn(&mut f); + mem::forget(f); // it doesnt like being remembered + out } /// Returns a slice containing the entire array. Equivalent to `&s[..]`. @@ -885,13 +903,11 @@ where /// not optimizing away. So if you give it a shot, make sure to watch what /// happens in the codegen tests. #[inline] -fn try_from_fn_erased( - buffer: &mut [MaybeUninit], - mut generator: impl FnMut(usize) -> R, -) -> ControlFlow -where - R: Try, -{ +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +const fn try_from_fn_erased>( + buffer: &mut [MaybeUninit], + mut generator: impl [const] FnMut(usize) -> R + [const] Destruct, +) -> ControlFlow { let mut guard = Guard { array_mut: buffer, initialized: 0 }; while guard.initialized < guard.array_mut.len() { @@ -930,7 +946,8 @@ impl Guard<'_, T> { /// /// No more than N elements must be initialized. #[inline] - pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + #[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] + pub(crate) const unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not // invoke this method more than N times then writes will be in-bounds // and slots will not be initialized more than once. @@ -941,11 +958,11 @@ impl Guard<'_, T> { } } -impl Drop for Guard<'_, T> { +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +impl const Drop for Guard<'_, T> { #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); - // SAFETY: this slice will contain only initialized objects. unsafe { self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop(); diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 204291886589e..bcff1d7f456ca 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -1,3 +1,4 @@ +use crate::marker::{Destruct, PhantomData}; use crate::ops::ControlFlow; /// The `?` operator and `try {}` blocks. @@ -396,6 +397,25 @@ pub(crate) type ChangeOutputType>, V> = /// Not currently planned to be exposed publicly, so just `pub(crate)`. #[repr(transparent)] pub(crate) struct NeverShortCircuit(pub T); +// FIXME(const-hack): replace with `|a| NeverShortCircuit(f(a))` when const closures added. +pub(crate) struct Wrapped T> { + f: F, + p: PhantomData<(T, A)>, +} +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl T + [const] Destruct> const FnOnce<(A,)> for Wrapped { + type Output = NeverShortCircuit; + + extern "rust-call" fn call_once(mut self, args: (A,)) -> Self::Output { + self.call_mut(args) + } +} +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl T> const FnMut<(A,)> for Wrapped { + extern "rust-call" fn call_mut(&mut self, (args,): (A,)) -> Self::Output { + NeverShortCircuit((self.f)(args)) + } +} impl NeverShortCircuit { /// Wraps a unary function to produce one that wraps the output into a `NeverShortCircuit`. @@ -403,10 +423,11 @@ impl NeverShortCircuit { /// This is useful for implementing infallible functions in terms of the `try_` ones, /// without accidentally capturing extra generic parameters in a closure. #[inline] - pub(crate) fn wrap_mut_1( - mut f: impl FnMut(A) -> T, - ) -> impl FnMut(A) -> NeverShortCircuit { - move |a| NeverShortCircuit(f(a)) + pub(crate) const fn wrap_mut_1(f: F) -> Wrapped + where + F: [const] FnMut(A) -> T, + { + Wrapped { f, p: PhantomData } } #[inline] @@ -417,7 +438,8 @@ impl NeverShortCircuit { pub(crate) enum NeverShortCircuitResidual {} -impl Try for NeverShortCircuit { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const Try for NeverShortCircuit { type Output = T; type Residual = NeverShortCircuitResidual; @@ -431,15 +453,15 @@ impl Try for NeverShortCircuit { NeverShortCircuit(x) } } - -impl FromResidual for NeverShortCircuit { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const FromResidual for NeverShortCircuit { #[inline] fn from_residual(never: NeverShortCircuitResidual) -> Self { match never {} } } - -impl Residual for NeverShortCircuitResidual { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const Residual for NeverShortCircuitResidual { type TryType = NeverShortCircuit; } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 7b0b609f0af64..436856635c1c8 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -17,8 +17,8 @@ #![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] -#![feature(const_cell_traits)] #![feature(const_array)] +#![feature(const_cell_traits)] #![feature(const_cmp)] #![feature(const_convert)] #![feature(const_destruct)] From e3a2c23e37f16e08b316cd83c9ee8875d826e9ff Mon Sep 17 00:00:00 2001 From: bendn Date: Mon, 24 Nov 2025 17:09:41 +0700 Subject: [PATCH 3/3] redo the drain --- library/core/src/array/drain.rs | 128 +++++++++++++++++++----------- library/core/src/array/mod.rs | 28 +++---- library/core/src/ops/try_trait.rs | 1 + library/coretests/tests/array.rs | 3 + 4 files changed, 95 insertions(+), 65 deletions(-) diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 6ab649c036b43..1c6137191324c 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -1,76 +1,108 @@ -use crate::assert_unsafe_precondition; -use crate::marker::Destruct; -use crate::mem::ManuallyDrop; +use crate::marker::{Destruct, PhantomData}; +use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst}; +use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, null_mut}; +impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { + /// This function returns a function that lets you index the given array in const. + /// As implemented it can optimize better than iterators, and can be constified. + /// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented + /// as it is a struct that implements const fn; + /// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`. + /// The only method you're really allowed to call is `next()`, + /// anything else is more or less UB, hence this function being unsafe. + /// Moved elements will not be dropped. + /// This will also not actually store the array. + /// + /// SAFETY: must only be called `N` times. Thou shalt not drop the array either. + // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] + pub(super) const unsafe fn new(array: &'l mut ManuallyDrop<[T; N]>, f: &'f mut F) -> Self { + // dont drop the array, transfers "ownership" to Self + let ptr: NonNull = NonNull::from_mut(array).cast(); + // SAFETY: + // Adding `slice.len()` to the starting pointer gives a pointer + // at the end of `slice`. `end` will never be dereferenced, only checked + // for direct pointer equality with `ptr` to check if the drainer is done. + unsafe { + let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) }; + Self { ptr, end, f, l: PhantomData } + } + } +} + +/// See [`Drain::new`]; this is our fake iterator. #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -pub(super) struct Drain<'a, T, U, const N: usize, F: FnMut(T) -> U> { - array: ManuallyDrop<[T; N]>, - moved: usize, - f: &'a mut F, +pub(super) struct Drain<'l, 'f, T, const N: usize, F> { + // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. + /// The pointer to the next element to return, or the past-the-end location + /// if the drainer is empty. + /// + /// This address will be used for all ZST elements, never changed. + /// As we "own" this array, we dont need to store any lifetime. + ptr: NonNull, + /// For non-ZSTs, the non-null pointer to the past-the-end element. + /// For ZSTs, this is null. + end: *mut T, + + f: &'f mut F, + l: PhantomData<&'l mut [T; N]>, } + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const FnOnce<(usize,)> for &mut Drain<'_, T, U, N, F> +impl const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F> where F: [const] FnMut(T) -> U, { type Output = U; + /// This implementation is useless. extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { self.call_mut(args) } } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl const FnMut<(usize,)> for &mut Drain<'_, T, U, N, F> +impl const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F> where F: [const] FnMut(T) -> U, { - extern "rust-call" fn call_mut(&mut self, (i,): (usize,)) -> Self::Output { - // SAFETY: increment moved before moving. if `f` panics, we drop the rest. - self.moved += 1; - assert_unsafe_precondition!( - check_library_ub, - "musnt index array out of bounds", (i: usize = i, size: usize = N) => i < size - ); - // SAFETY: the `i` should also always go up, and musnt skip any, else some things will be leaked. - // SAFETY: if it goes down, we will drop freed elements. not good. - // SAFETY: caller guarantees never called with number >= N (see `Drain::new`) - (self.f)(unsafe { self.array.as_ptr().add(i).read() }) + // FIXME(const-hack): ideally this would be an unsafe fn `next()`, and to use it you would instead `|_| unsafe { drain.next() }`. + extern "rust-call" fn call_mut( + &mut self, + (_ /* ignore argument */,): (usize,), + ) -> Self::Output { + if T::IS_ZST { + // its UB to call this more than N times, so returning more ZSTs is valid. + // SAFETY: its a ZST? we conjur. + (self.f)(unsafe { conjure_zst::() }) + } else { + // increment before moving; if `f` panics, we drop the rest. + let p = self.ptr; + // SAFETY: caller guarantees never called more than N times (see `Drain::new`) + self.ptr = unsafe { self.ptr.add(1) }; + // SAFETY: we are allowed to move this. + (self.f)(unsafe { p.read() }) + } } } #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] -impl U> const Drop - for Drain<'_, T, U, N, F> -{ +impl const Drop for Drain<'_, '_, T, N, F> { fn drop(&mut self) { - let mut n = self.moved; - while n != N { - // SAFETY: moved must always be < N - unsafe { self.array.as_mut_ptr().add(n).drop_in_place() }; - n += 1; + if !T::IS_ZST { + // SAFETY: we cant read more than N elements + let slice = unsafe { + from_raw_parts_mut::<[T]>( + self.ptr.as_ptr(), + // SAFETY: `start <= end` + self.end.offset_from_unsigned(self.ptr.as_ptr()), + ) + }; + + // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) + unsafe { drop_in_place(slice) } } } } -impl<'a, T, U, const N: usize, F: FnMut(T) -> U> Drain<'a, T, U, N, F> { - /// This function returns a function that lets you index the given array in const. - /// As implemented it can optimize better than iterators, and can be constified. - /// It acts like a sort of guard and iterator combined, which can be implemented - /// as it is a struct that implements const fn; - /// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`. - /// The only method you're really allowed to call is `next()`, - /// anything else is more or less UB, hence this function being unsafe. - /// Moved elements will not be dropped. - /// - /// Previously this was implemented as a wrapper around a `slice::Iter`, which - /// called `read()` on the returned `&T`; gnarly stuff. - /// - /// SAFETY: must be called in order of 0..N, without indexing out of bounds. (see `Drain::call_mut`) - /// Potentially the function could completely disregard the supplied argument, however i think that behaviour would be unintuitive. - // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. - pub(super) const unsafe fn new(array: [T; N], f: &'a mut F) -> Self { - Self { array: ManuallyDrop::new(array), moved: 0, f } - } -} diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index bbf75c70d6963..c8d0622059cde 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -13,7 +13,7 @@ use crate::hash::{self, Hash}; use crate::intrinsics::transmute_unchecked; use crate::iter::{UncheckedIterator, repeat_n}; use crate::marker::Destruct; -use crate::mem::{self, MaybeUninit}; +use crate::mem::{self, ManuallyDrop, MaybeUninit}; use crate::ops::{ ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try, }; @@ -145,14 +145,10 @@ where #[inline] #[unstable(feature = "array_try_from_fn", issue = "89379")] #[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] -pub const fn try_from_fn( - cb: impl [const] FnMut(usize) -> R + [const] Destruct, -) -> ChangeOutputType +pub const fn try_from_fn(cb: F) -> ChangeOutputType where - R: [const] Try< - Residual: [const] Residual<[R::Output; N], TryType: [const] Try>, - Output: [const] Destruct, - >, + R: [const] Try, Output: [const] Destruct>, + F: [const] FnMut(usize) -> R + [const] Destruct, { let mut array = [const { MaybeUninit::uninit() }; N]; match try_from_fn_erased(&mut array, cb) { @@ -559,6 +555,7 @@ impl [T; N] { where F: [const] FnMut(T) -> U + [const] Destruct, U: [const] Destruct, + T: [const] Destruct, { self.try_map(NeverShortCircuit::wrap_mut_1(f)).0 } @@ -600,16 +597,13 @@ impl [T; N] { mut f: impl [const] FnMut(T) -> R + [const] Destruct, ) -> ChangeOutputType where - R: [const] Try< - Residual: [const] Residual<[R::Output; N], TryType: [const] Try>, - Output: [const] Destruct, - >, + R: [const] Try, Output: [const] Destruct>, + T: [const] Destruct, { - // SAFETY: try_from_fn calls `f` with 0..N. - let mut f = unsafe { drain::Drain::new(self, &mut f) }; - let out = try_from_fn(&mut f); - mem::forget(f); // it doesnt like being remembered - out + let mut me = ManuallyDrop::new(self); + // SAFETY: try_from_fn calls `f` N times. + let mut f = unsafe { drain::Drain::new(&mut me, &mut f) }; + try_from_fn(&mut f) } /// Returns a slice containing the entire array. Equivalent to `&s[..]`. diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index bcff1d7f456ca..34000f6d6b218 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -364,6 +364,7 @@ where pub const trait Residual: Sized { /// The "return" type of this meta-function. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] + // FIXME: ought to be implied type TryType: [const] Try; } diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index bdea7d4c0bdb1..2b4429092e98b 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -737,4 +737,7 @@ fn const_array_ops() { assert_eq!(const { [5, 6, 1, 2].map(doubler) }, [10, 12, 2, 4]); assert_eq!(const { [1, usize::MAX, 2, 8].try_map(maybe_doubler) }, None); assert_eq!(const { std::array::try_from_fn::<_, 5, _>(maybe_doubler) }, Some([0, 2, 4, 6, 8])); + #[derive(Debug, PartialEq)] + struct Zst; + assert_eq!([(); 10].try_map(|()| Some(Zst)), Some([const { Zst }; 10])); }