Skip to content

Commit e0a2de4

Browse files
committed
FEAT: Add Zip::apply_assign_into
1 parent 07cf42d commit e0a2de4

File tree

5 files changed

+95
-8
lines changed

5 files changed

+95
-8
lines changed

src/argument_traits.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use std::cell::Cell;
2+
use std::mem::MaybeUninit;
3+
4+
5+
/// A producer element that can be assigned to once
6+
pub trait AssignElem<T> {
7+
/// Assign the value `input` to the element that self represents.
8+
fn assign_elem(self, input: T);
9+
}
10+
11+
/// Assignable element, simply `*self = input`.
12+
impl<'a, T> AssignElem<T> for &'a mut T {
13+
fn assign_elem(self, input: T) {
14+
*self = input;
15+
}
16+
}
17+
18+
/// Assignable element, simply `self.set(input)`.
19+
impl<'a, T> AssignElem<T> for &'a Cell<T> {
20+
fn assign_elem(self, input: T) {
21+
self.set(input);
22+
}
23+
}
24+
25+
/// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not
26+
/// read or dropped).
27+
impl<'a, T> AssignElem<T> for &'a mut MaybeUninit<T> {
28+
fn assign_elem(self, input: T) {
29+
*self = MaybeUninit::new(input);
30+
}
31+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ mod array_approx;
149149
mod array_serde;
150150
mod arrayformat;
151151
mod arraytraits;
152+
mod argument_traits;
153+
pub use crate::argument_traits::AssignElem;
152154
mod data_traits;
153155

154156
pub use crate::aliases::*;

src/zip/mod.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
mod zipmacro;
1111

1212
use crate::imp_prelude::*;
13+
use crate::AssignElem;
1314
use crate::IntoDimension;
1415
use crate::Layout;
1516
use crate::NdIndex;
@@ -579,6 +580,7 @@ pub struct Zip<Parts, D> {
579580
layout: Layout,
580581
}
581582

583+
582584
impl<P, D> Zip<(P,), D>
583585
where
584586
D: Dimension,
@@ -990,21 +992,36 @@ macro_rules! map_impl {
990992
///
991993
/// Restricted to functions that produce copyable results for technical reasons; other
992994
/// cases are not yet implemented.
993-
pub fn apply_collect<R>(self, mut f: impl FnMut($($p::Item,)* ) -> R) -> Array<R, D>
995+
pub fn apply_collect<R>(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array<R, D>
994996
where R: Copy,
995997
{
998+
// To support non-Copy elements, implementation of dropping partial array (on
999+
// panic) is needed
1000+
let is_c = self.layout.is(CORDER);
1001+
let is_f = !is_c && self.layout.is(FORDER);
1002+
let mut output = Array::maybe_uninit(self.dimension.clone().set_f(is_f));
1003+
self.apply_assign_into(&mut output, f);
9961004
unsafe {
997-
let is_c = self.layout.is(CORDER);
998-
let is_f = !is_c && self.layout.is(FORDER);
999-
let mut output = Array::maybe_uninit(self.dimension.clone().set_f(is_f));
1000-
self.and(&mut output)
1001-
.apply(move |$($p, )* output_| {
1002-
std::ptr::write(output_.as_mut_ptr(), f($($p ),*));
1003-
});
10041005
output.assume_init()
10051006
}
10061007
}
10071008

1009+
/// Apply and assign the results into the producer `into`, which should have the same
1010+
/// size as the other inputs.
1011+
///
1012+
/// The producer should have assignable items as dictated by the `AssignElem` trait,
1013+
/// for example `&mut R`.
1014+
pub fn apply_assign_into<R, Q>(self, into: Q, mut f: impl FnMut($($p::Item,)* ) -> R)
1015+
where Q: IntoNdProducer<Dim=D>,
1016+
Q::Item: AssignElem<R>
1017+
{
1018+
self.and(into)
1019+
.apply(move |$($p, )* output_| {
1020+
output_.assign_elem(f($($p ),*));
1021+
});
1022+
}
1023+
1024+
10081025
);
10091026

10101027
/// Split the `Zip` evenly in two.

src/zip/zipmacro.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ macro_rules! azip {
122122
$(.and($prod))*
123123
.$apply(|$first_pat, $($pat),*| $body)
124124
};
125+
126+
// Unindexed with one or more producer, no loop body
127+
(@build $apply:ident $first_prod:expr $(, $prod:expr)* $(,)?) => {
128+
$crate::Zip::from($first_prod)
129+
$(.and($prod))*
130+
};
125131
// catch-all rule
126132
(@build $($t:tt)*) => { compile_error!("Invalid syntax in azip!()") };
127133
($($t:tt)*) => {

tests/azip.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::iter::FromIterator;
1212

1313
use itertools::{assert_equal, cloned};
1414

15+
use std::cell::Cell;
1516
use std::mem::swap;
1617

1718
#[test]
@@ -77,6 +78,36 @@ fn test_zip_collect() {
7778
}
7879
}
7980

81+
#[test]
82+
#[cfg(feature = "approx")]
83+
fn test_zip_assign_into() {
84+
use approx::assert_abs_diff_eq;
85+
86+
let mut a = Array::<f32, _>::zeros((5, 10));
87+
let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32);
88+
let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32));
89+
90+
Zip::from(&b).and(&c).apply_assign_into(&mut a, |x, y| x + y);
91+
92+
assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6);
93+
}
94+
95+
#[test]
96+
#[cfg(feature = "approx")]
97+
fn test_zip_assign_into_cell() {
98+
use approx::assert_abs_diff_eq;
99+
100+
let a = Array::<Cell<f32>, _>::default((5, 10));
101+
let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32);
102+
let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32));
103+
104+
Zip::from(&b).and(&c).apply_assign_into(&a, |x, y| x + y);
105+
let a2 = a.mapv(|elt| elt.get());
106+
107+
assert_abs_diff_eq!(a2, &b + &c, epsilon = 1e-6);
108+
}
109+
110+
80111
#[test]
81112
fn test_azip_syntax_trailing_comma() {
82113
let mut b = Array::<i32, _>::zeros((5, 5));

0 commit comments

Comments
 (0)