Skip to content

Commit 10ba041

Browse files
committed
Make mapv_into_any() work for ArcArray, resolves #1280
1 parent 492b274 commit 10ba041

File tree

2 files changed

+95
-12
lines changed

2 files changed

+95
-12
lines changed

src/impl_methods.rs

+59-10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::mem::{size_of, ManuallyDrop};
1717
use crate::imp_prelude::*;
1818

1919
use crate::argument_traits::AssignElem;
20+
use crate::data_traits::RawDataSubst;
2021
use crate::dimension;
2122
use crate::dimension::broadcast::co_broadcast;
2223
use crate::dimension::reshape_dim;
@@ -2814,15 +2815,59 @@ where
28142815
/// map is performed as in [`mapv`].
28152816
///
28162817
/// Elements are visited in arbitrary order.
2817-
///
2818+
///
2819+
/// Example:
2820+
///
2821+
/// ```rust
2822+
/// # use ndarray::{array, Array};
2823+
/// let a: Array<f32, _> = array![[1., 2., 3.]];
2824+
/// let b = a.clone();
2825+
/// // Same type, no new memory allocation.
2826+
/// let a_plus_one = a.mapv_into_any(|a| a + 1.);
2827+
/// // Different types, allocates new memory.
2828+
/// let rounded = b.mapv_into_any(|a| a.round() as i32);
2829+
/// ```
2830+
///
2831+
/// Note that this method works on arrays with different memory
2832+
/// representations (e.g. [`OwnedRepr`](crate::OwnedRepr) vs
2833+
/// [`OwnedArcRepr`](crate::OwnedArcRepr)) but it does *not* convert between
2834+
/// different memory representations.
2835+
///
2836+
/// This compiles:
2837+
/// ```rust
2838+
/// # use ndarray::{array, ArcArray};
2839+
/// let a: ArcArray<f32, _> = array![[1., 2., 3.]].into();
2840+
/// // OwnedArcRepr --> OwnedArcRepr.
2841+
/// let a_plus_one = a.mapv_into_any(|a| a + 1.);
2842+
/// // We can convert to OwnedRepr if we want.
2843+
/// let a_plus_one = a_plus_one.into_owned();
2844+
/// ```
2845+
///
2846+
/// This fails to compile:
2847+
/// ```compile_fail,E0308
2848+
/// # use ndarray::{array, Array, ArcArray};
2849+
/// let a: ArcArray<f32, _> = array![[1., 2., 3.]].into();
2850+
/// // OwnedArcRepr --> OwnedRepr
2851+
/// let a_plus_one: Array<_, _> = a.mapv_into_any(|a| a + 1.);
2852+
/// ```
2853+
///
28182854
/// [`mapv_into`]: ArrayBase::mapv_into
28192855
/// [`mapv`]: ArrayBase::mapv
2820-
pub fn mapv_into_any<B, F>(self, mut f: F) -> Array<B, D>
2856+
pub fn mapv_into_any<B, F>(self, mut f: F) -> ArrayBase<<S as RawDataSubst<B>>::Output, D>
28212857
where
2822-
S: DataMut,
2858+
// Output is same memory representation as input,
2859+
// Substituting B for A.
2860+
// Need 'static lifetime bounds for TypeId to work.
2861+
S: DataMut<Elem = A> + RawDataSubst<B> + 'static,
2862+
// Mapping function maps from A to B.
28232863
F: FnMut(A) -> B,
2864+
// Need 'static lifetime bounds for TypeId to work.
2865+
// mapv() requires that A be Clone.
28242866
A: Clone + 'static,
28252867
B: 'static,
2868+
// mapv() always returns ArrayBase<OwnedRepr<_>,_>
2869+
// This bound ensures we can convert from OwnedRepr to the output repr.
2870+
ArrayBase<<S as RawDataSubst<B>>::Output, D>: From<Array<B,D>>,
28262871
{
28272872
if core::any::TypeId::of::<A>() == core::any::TypeId::of::<B>() {
28282873
// A and B are the same type.
@@ -2832,16 +2877,20 @@ where
28322877
// Safe because A and B are the same type.
28332878
unsafe { unlimited_transmute::<B, A>(b) }
28342879
};
2835-
// Delegate to mapv_into() using the wrapped closure.
2836-
// Convert output to a uniquely owned array of type Array<A, D>.
2837-
let output = self.mapv_into(f).into_owned();
2838-
// Change the return type from Array<A, D> to Array<B, D>.
2839-
// Again, safe because A and B are the same type.
2840-
unsafe { unlimited_transmute::<Array<A, D>, Array<B, D>>(output) }
2880+
// Delegate to mapv_into() to map from element type A to type A.
2881+
let output = self.mapv_into(f);
2882+
// If A and B are the same type, and if the input and output arrays
2883+
// have the same kind of memory representation (OwnedRepr vs
2884+
// OwnedArcRepr), then their memory representations should be the
2885+
// same type, e.g. OwnedRepr<A> == OwnedRepr<B>
2886+
debug_assert!(core::any::TypeId::of::<S>() == core::any::TypeId::of::<<S as RawDataSubst<B>>::Output>());
2887+
// Now we can safely transmute the element type from A to the
2888+
// identical type B, keeping the same memory representation.
2889+
unsafe { unlimited_transmute::<ArrayBase<S, D>, ArrayBase<<S as RawDataSubst<B>>::Output, D>>(output) }
28412890
} else {
28422891
// A and B are not the same type.
28432892
// Fallback to mapv().
2844-
self.mapv(f)
2893+
self.mapv(f).into()
28452894
}
28462895
}
28472896

tests/array.rs

+36-2
Original file line numberDiff line numberDiff line change
@@ -1054,15 +1054,49 @@ fn mapv_into_any_same_type()
10541054
{
10551055
let a: Array<f64, _> = array![[1., 2., 3.], [4., 5., 6.]];
10561056
let a_plus_one: Array<f64, _> = array![[2., 3., 4.], [5., 6., 7.]];
1057-
assert_eq!(a.mapv_into_any(|a| a + 1.), a_plus_one);
1057+
let b = a.mapv_into_any(|a| a + 1.);
1058+
assert_eq!(b, a_plus_one);
10581059
}
10591060

10601061
#[test]
10611062
fn mapv_into_any_diff_types()
10621063
{
10631064
let a: Array<f64, _> = array![[1., 2., 3.], [4., 5., 6.]];
10641065
let a_even: Array<bool, _> = array![[false, true, false], [true, false, true]];
1065-
assert_eq!(a.mapv_into_any(|a| a.round() as i32 % 2 == 0), a_even);
1066+
let b = a.mapv_into_any(|a| a.round() as i32 % 2 == 0);
1067+
assert_eq!(b, a_even);
1068+
}
1069+
1070+
#[test]
1071+
fn mapv_into_any_arcarray_same_type() {
1072+
let a: ArcArray<f64, _> = array![[1., 2., 3.], [4., 5., 6.]].into_shared();
1073+
let a_plus_one: Array<f64, _> = array![[2., 3., 4.], [5., 6., 7.]];
1074+
let b = a.mapv_into_any(|a| a + 1.);
1075+
assert_eq!(b, a_plus_one);
1076+
}
1077+
1078+
#[test]
1079+
fn mapv_into_any_arcarray_diff_types() {
1080+
let a: ArcArray<f64, _> = array![[1., 2., 3.], [4., 5., 6.]].into_shared();
1081+
let a_even: Array<bool, _> = array![[false, true, false], [true, false, true]];
1082+
let b = a.mapv_into_any(|a| a.round() as i32 % 2 == 0);
1083+
assert_eq!(b, a_even);
1084+
}
1085+
1086+
#[test]
1087+
fn mapv_into_any_cowarray_same_type() {
1088+
let a: CowArray<f64, _> = array![[1., 2., 3.], [4., 5., 6.]].into();
1089+
let a_plus_one: Array<f64, _> = array![[2., 3., 4.], [5., 6., 7.]];
1090+
let b = a.mapv_into_any(|a| a + 1.);
1091+
assert_eq!(b, a_plus_one);
1092+
}
1093+
1094+
#[test]
1095+
fn mapv_into_any_cowarray_diff_types() {
1096+
let a: CowArray<f64, _> = array![[1., 2., 3.], [4., 5., 6.]].into();
1097+
let a_even: Array<bool, _> = array![[false, true, false], [true, false, true]];
1098+
let b = a.mapv_into_any(|a| a.round() as i32 % 2 == 0);
1099+
assert_eq!(b, a_even);
10661100
}
10671101

10681102
#[test]

0 commit comments

Comments
 (0)