mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
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
This commit is contained in:
commit
a0c290f951
@ -364,3 +364,66 @@ macro_rules! array_impl_default {
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[lang = "array"]
|
||||
impl<T, const N: usize> [T; N] {
|
||||
/// Returns an array of the same size as `self`, with function `f` applied to each element
|
||||
/// in order.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(array_map)]
|
||||
/// let x = [1, 2, 3];
|
||||
/// let y = x.map(|v| v + 1);
|
||||
/// assert_eq!(y, [2, 3, 4]);
|
||||
///
|
||||
/// let x = [1, 2, 3];
|
||||
/// let mut temp = 0;
|
||||
/// let y = x.map(|v| { temp += 1; v * temp });
|
||||
/// assert_eq!(y, [1, 4, 9]);
|
||||
///
|
||||
/// let x = ["Ferris", "Bueller's", "Day", "Off"];
|
||||
/// let y = x.map(|v| v.len());
|
||||
/// assert_eq!(y, [6, 9, 3, 3]);
|
||||
/// ```
|
||||
#[unstable(feature = "array_map", issue = "75243")]
|
||||
pub fn map<F, U>(self, mut f: F) -> [U; N]
|
||||
where
|
||||
F: FnMut(T) -> U,
|
||||
{
|
||||
use crate::mem::MaybeUninit;
|
||||
struct Guard<T, const N: usize> {
|
||||
dst: *mut T,
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Drop for Guard<T, N> {
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.initialized <= N);
|
||||
|
||||
let initialized_part =
|
||||
crate::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
|
||||
// SAFETY: this raw slice will contain only initialized objects
|
||||
// that's why, it is allowed to drop it.
|
||||
unsafe {
|
||||
crate::ptr::drop_in_place(initialized_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut dst = MaybeUninit::uninit_array::<N>();
|
||||
let mut guard: Guard<U, N> =
|
||||
Guard { dst: MaybeUninit::first_ptr_mut(&mut dst), initialized: 0 };
|
||||
for (src, dst) in IntoIter::new(self).zip(&mut dst) {
|
||||
dst.write(f(src));
|
||||
guard.initialized += 1;
|
||||
}
|
||||
// FIXME: Convert to crate::mem::transmute once it works with generics.
|
||||
// unsafe { crate::mem::transmute::<[MaybeUninit<U>; N], [U; N]>(dst) }
|
||||
crate::mem::forget(guard);
|
||||
// SAFETY: At this point we've properly initialized the whole array
|
||||
// and we just need to cast it to the correct type.
|
||||
unsafe { crate::mem::transmute_copy::<_, [U; N]>(&dst) }
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +145,7 @@
|
||||
#![feature(abi_unadjusted)]
|
||||
#![feature(adx_target_feature)]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(maybe_uninit_extra)]
|
||||
#![feature(external_doc)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(const_caller_location)]
|
||||
|
@ -290,3 +290,43 @@ fn empty_array_is_always_default() {
|
||||
|
||||
let _arr = <[DoesNotImplDefault; 0]>::default();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_map() {
|
||||
let a = [1, 2, 3];
|
||||
let b = a.map(|v| v + 1);
|
||||
assert_eq!(b, [2, 3, 4]);
|
||||
|
||||
let a = [1u8, 2, 3];
|
||||
let b = a.map(|v| v as u64);
|
||||
assert_eq!(b, [1, 2, 3]);
|
||||
}
|
||||
|
||||
// See note on above test for why `should_panic` is used.
|
||||
#[test]
|
||||
#[should_panic(expected = "test succeeded")]
|
||||
fn array_map_drop_safety() {
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use core::sync::atomic::Ordering;
|
||||
static DROPPED: AtomicUsize = AtomicUsize::new(0);
|
||||
struct DropCounter;
|
||||
impl Drop for DropCounter {
|
||||
fn drop(&mut self) {
|
||||
DROPPED.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let num_to_create = 5;
|
||||
let success = std::panic::catch_unwind(|| {
|
||||
let items = [0; 10];
|
||||
let mut nth = 0;
|
||||
items.map(|_| {
|
||||
assert!(nth < num_to_create);
|
||||
nth += 1;
|
||||
DropCounter
|
||||
});
|
||||
});
|
||||
assert!(success.is_err());
|
||||
assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create);
|
||||
panic!("test succeeded")
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![feature(alloc_layout_extra)]
|
||||
#![feature(array_chunks)]
|
||||
#![feature(array_map)]
|
||||
#![feature(bool_to_option)]
|
||||
#![feature(bound_cloned)]
|
||||
#![feature(box_syntax)]
|
||||
|
@ -165,6 +165,7 @@ language_item_table! {
|
||||
BoolImplItem, sym::bool, bool_impl, Target::Impl;
|
||||
CharImplItem, sym::char, char_impl, Target::Impl;
|
||||
StrImplItem, sym::str, str_impl, Target::Impl;
|
||||
ArrayImplItem, sym::array, array_impl, Target::Impl;
|
||||
SliceImplItem, sym::slice, slice_impl, Target::Impl;
|
||||
SliceU8ImplItem, sym::slice_u8, slice_u8_impl, Target::Impl;
|
||||
StrAllocImplItem, sym::str_alloc, str_alloc_impl, Target::Impl;
|
||||
|
@ -649,6 +649,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
self.assemble_inherent_impl_for_primitive(lang_def_id);
|
||||
}
|
||||
}
|
||||
ty::Array(_, _) => {
|
||||
let lang_def_id = lang_items.array_impl();
|
||||
self.assemble_inherent_impl_for_primitive(lang_def_id);
|
||||
}
|
||||
ty::RawPtr(ty::TypeAndMut { ty: _, mutbl }) => {
|
||||
let (lang_def_id1, lang_def_id2) = match mutbl {
|
||||
hir::Mutability::Not => {
|
||||
|
@ -112,6 +112,16 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> {
|
||||
item.span,
|
||||
);
|
||||
}
|
||||
ty::Array(_, _) => {
|
||||
self.check_primitive_impl(
|
||||
def_id,
|
||||
lang_items.array_impl(),
|
||||
None,
|
||||
"array",
|
||||
"[T; N]",
|
||||
item.span,
|
||||
);
|
||||
}
|
||||
ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not })
|
||||
if matches!(inner.kind, ty::Slice(_)) =>
|
||||
{
|
||||
|
@ -388,7 +388,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V
|
||||
Bool => tcx.lang_items().bool_impl(),
|
||||
Str => tcx.lang_items().str_impl(),
|
||||
Slice => tcx.lang_items().slice_impl(),
|
||||
Array => tcx.lang_items().slice_impl(),
|
||||
Array => tcx.lang_items().array_impl(),
|
||||
Tuple => None,
|
||||
Unit => None,
|
||||
RawPointer => tcx.lang_items().const_ptr_impl(),
|
||||
|
@ -55,6 +55,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
|
||||
lang_items.bool_impl(),
|
||||
lang_items.char_impl(),
|
||||
lang_items.str_impl(),
|
||||
lang_items.array_impl(),
|
||||
lang_items.slice_impl(),
|
||||
lang_items.slice_u8_impl(),
|
||||
lang_items.str_alloc_impl(),
|
||||
|
Loading…
Reference in New Issue
Block a user