Skip to content

Commit 57c5902

Browse files
committed
Auto merge of rust-lang#75212 - JulianKnodt:array_map, r=LukasKalbertodt
Add `array` lang item and `[T; N]::map(f: FnMut(T) -> S)` This introduces an `array` lang item so functions can be defined on top of `[T; N]`. This was previously not done because const-generics was not complete enough to allow for this. Now it is in a state that is usable enough to start adding functions. The function added is a monadic (I think?) map from `[T; N] -> [S; N]`. Until transmute can function on arrays, it also allocates an extra temporary array, but this can be removed at some point. r? @lcnr
2 parents adb0a53 + f632c1a commit 57c5902

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

core/src/array/mod.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,3 +364,66 @@ macro_rules! array_impl_default {
364364
}
365365

366366
array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}
367+
368+
#[cfg(not(bootstrap))]
369+
#[lang = "array"]
370+
impl<T, const N: usize> [T; N] {
371+
/// Returns an array of the same size as `self`, with function `f` applied to each element
372+
/// in order.
373+
///
374+
/// # Examples
375+
///
376+
/// ```
377+
/// #![feature(array_map)]
378+
/// let x = [1, 2, 3];
379+
/// let y = x.map(|v| v + 1);
380+
/// assert_eq!(y, [2, 3, 4]);
381+
///
382+
/// let x = [1, 2, 3];
383+
/// let mut temp = 0;
384+
/// let y = x.map(|v| { temp += 1; v * temp });
385+
/// assert_eq!(y, [1, 4, 9]);
386+
///
387+
/// let x = ["Ferris", "Bueller's", "Day", "Off"];
388+
/// let y = x.map(|v| v.len());
389+
/// assert_eq!(y, [6, 9, 3, 3]);
390+
/// ```
391+
#[unstable(feature = "array_map", issue = "75243")]
392+
pub fn map<F, U>(self, mut f: F) -> [U; N]
393+
where
394+
F: FnMut(T) -> U,
395+
{
396+
use crate::mem::MaybeUninit;
397+
struct Guard<T, const N: usize> {
398+
dst: *mut T,
399+
initialized: usize,
400+
}
401+
402+
impl<T, const N: usize> Drop for Guard<T, N> {
403+
fn drop(&mut self) {
404+
debug_assert!(self.initialized <= N);
405+
406+
let initialized_part =
407+
crate::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
408+
// SAFETY: this raw slice will contain only initialized objects
409+
// that's why, it is allowed to drop it.
410+
unsafe {
411+
crate::ptr::drop_in_place(initialized_part);
412+
}
413+
}
414+
}
415+
let mut dst = MaybeUninit::uninit_array::<N>();
416+
let mut guard: Guard<U, N> =
417+
Guard { dst: MaybeUninit::first_ptr_mut(&mut dst), initialized: 0 };
418+
for (src, dst) in IntoIter::new(self).zip(&mut dst) {
419+
dst.write(f(src));
420+
guard.initialized += 1;
421+
}
422+
// FIXME: Convert to crate::mem::transmute once it works with generics.
423+
// unsafe { crate::mem::transmute::<[MaybeUninit<U>; N], [U; N]>(dst) }
424+
crate::mem::forget(guard);
425+
// SAFETY: At this point we've properly initialized the whole array
426+
// and we just need to cast it to the correct type.
427+
unsafe { crate::mem::transmute_copy::<_, [U; N]>(&dst) }
428+
}
429+
}

core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
#![feature(abi_unadjusted)]
146146
#![feature(adx_target_feature)]
147147
#![feature(maybe_uninit_slice)]
148+
#![feature(maybe_uninit_extra)]
148149
#![feature(external_doc)]
149150
#![feature(associated_type_bounds)]
150151
#![feature(const_caller_location)]

core/tests/array.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,43 @@ fn empty_array_is_always_default() {
290290

291291
let _arr = <[DoesNotImplDefault; 0]>::default();
292292
}
293+
294+
#[test]
295+
fn array_map() {
296+
let a = [1, 2, 3];
297+
let b = a.map(|v| v + 1);
298+
assert_eq!(b, [2, 3, 4]);
299+
300+
let a = [1u8, 2, 3];
301+
let b = a.map(|v| v as u64);
302+
assert_eq!(b, [1, 2, 3]);
303+
}
304+
305+
// See note on above test for why `should_panic` is used.
306+
#[test]
307+
#[should_panic(expected = "test succeeded")]
308+
fn array_map_drop_safety() {
309+
use core::sync::atomic::AtomicUsize;
310+
use core::sync::atomic::Ordering;
311+
static DROPPED: AtomicUsize = AtomicUsize::new(0);
312+
struct DropCounter;
313+
impl Drop for DropCounter {
314+
fn drop(&mut self) {
315+
DROPPED.fetch_add(1, Ordering::SeqCst);
316+
}
317+
}
318+
319+
let num_to_create = 5;
320+
let success = std::panic::catch_unwind(|| {
321+
let items = [0; 10];
322+
let mut nth = 0;
323+
items.map(|_| {
324+
assert!(nth < num_to_create);
325+
nth += 1;
326+
DropCounter
327+
});
328+
});
329+
assert!(success.is_err());
330+
assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create);
331+
panic!("test succeeded")
332+
}

core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(alloc_layout_extra)]
22
#![feature(array_chunks)]
3+
#![feature(array_map)]
34
#![feature(bool_to_option)]
45
#![feature(bound_cloned)]
56
#![feature(box_syntax)]

0 commit comments

Comments
 (0)