Skip to content

Commit a0c290f

Browse files
committed
Auto merge of #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 1a7d9f5 + af32db2 commit a0c290f

File tree

9 files changed

+122
-1
lines changed

9 files changed

+122
-1
lines changed

library/core/src/array/mod.rs

+63
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+
}

library/core/src/lib.rs

+1
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)]

library/core/tests/array.rs

+40
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+
}

library/core/tests/lib.rs

+1
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)]

src/librustc_hir/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ language_item_table! {
165165
BoolImplItem, sym::bool, bool_impl, Target::Impl;
166166
CharImplItem, sym::char, char_impl, Target::Impl;
167167
StrImplItem, sym::str, str_impl, Target::Impl;
168+
ArrayImplItem, sym::array, array_impl, Target::Impl;
168169
SliceImplItem, sym::slice, slice_impl, Target::Impl;
169170
SliceU8ImplItem, sym::slice_u8, slice_u8_impl, Target::Impl;
170171
StrAllocImplItem, sym::str_alloc, str_alloc_impl, Target::Impl;

src/librustc_typeck/check/method/probe.rs

+4
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
649649
self.assemble_inherent_impl_for_primitive(lang_def_id);
650650
}
651651
}
652+
ty::Array(_, _) => {
653+
let lang_def_id = lang_items.array_impl();
654+
self.assemble_inherent_impl_for_primitive(lang_def_id);
655+
}
652656
ty::RawPtr(ty::TypeAndMut { ty: _, mutbl }) => {
653657
let (lang_def_id1, lang_def_id2) = match mutbl {
654658
hir::Mutability::Not => {

src/librustc_typeck/coherence/inherent_impls.rs

+10
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
112112
item.span,
113113
);
114114
}
115+
ty::Array(_, _) => {
116+
self.check_primitive_impl(
117+
def_id,
118+
lang_items.array_impl(),
119+
None,
120+
"array",
121+
"[T; N]",
122+
item.span,
123+
);
124+
}
115125
ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not })
116126
if matches!(inner.kind, ty::Slice(_)) =>
117127
{

src/librustdoc/clean/utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V
388388
Bool => tcx.lang_items().bool_impl(),
389389
Str => tcx.lang_items().str_impl(),
390390
Slice => tcx.lang_items().slice_impl(),
391-
Array => tcx.lang_items().slice_impl(),
391+
Array => tcx.lang_items().array_impl(),
392392
Tuple => None,
393393
Unit => None,
394394
RawPointer => tcx.lang_items().const_ptr_impl(),

src/librustdoc/passes/collect_trait_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
5555
lang_items.bool_impl(),
5656
lang_items.char_impl(),
5757
lang_items.str_impl(),
58+
lang_items.array_impl(),
5859
lang_items.slice_impl(),
5960
lang_items.slice_u8_impl(),
6061
lang_items.str_alloc_impl(),

0 commit comments

Comments
 (0)