Skip to content

Commit 0265666

Browse files
committed
Add multislice! macro
1 parent a7f8d58 commit 0265666

File tree

3 files changed

+386
-1
lines changed

3 files changed

+386
-1
lines changed

src/lib.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,10 +470,13 @@ pub type Ixs = isize;
470470
/// [`.slice_move()`]: #method.slice_move
471471
/// [`.slice_collapse()`]: #method.slice_collapse
472472
///
473+
/// It's possible to take multiple simultaneous *mutable* slices with the
474+
/// [`multislice!()`](macro.multislice!.html) macro.
475+
///
473476
/// ```
474477
/// extern crate ndarray;
475478
///
476-
/// use ndarray::{arr2, arr3, s};
479+
/// use ndarray::{arr2, arr3, multislice, s};
477480
///
478481
/// fn main() {
479482
///
@@ -520,6 +523,20 @@ pub type Ixs = isize;
520523
/// [12, 11, 10]]);
521524
/// assert_eq!(f, g);
522525
/// assert_eq!(f.shape(), &[2, 3]);
526+
///
527+
/// // Let's take two disjoint, mutable slices of a matrix with
528+
/// //
529+
/// // - One containing all the even-index columns in the matrix
530+
/// // - One containing all the odd-index columns in the matrix
531+
/// let mut h = arr2(&[[0, 1, 2, 3],
532+
/// [4, 5, 6, 7]]);
533+
/// let (s0, s1) = multislice!(h, (mut s![.., ..;2], mut s![.., 1..;2]));
534+
/// let i = arr2(&[[0, 2],
535+
/// [4, 6]]);
536+
/// let j = arr2(&[[1, 3],
537+
/// [5, 7]]);
538+
/// assert_eq!(s0, i);
539+
/// assert_eq!(s1, j);
523540
/// }
524541
/// ```
525542
///

src/slice.rs

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,3 +623,226 @@ macro_rules! s(
623623
&*&$crate::s![@parse ::std::marker::PhantomData::<$crate::Ix0>, [] $($t)*]
624624
};
625625
);
626+
627+
/// Take multiple slices simultaneously.
628+
///
629+
/// This macro makes it possible to take multiple slices of the same array, as
630+
/// long as Rust's aliasing rules are followed for *elements* in the slices.
631+
/// For example, it's possible to take two disjoint, mutable slices of an
632+
/// array, with one referencing the even-index elements and the other
633+
/// referencing the odd-index elements. If you tried to achieve this by calling
634+
/// `.slice_mut()` twice, the borrow checker would complain about mutably
635+
/// borrowing the array twice (even though it's safe as long as the slices are
636+
/// disjoint).
637+
///
638+
/// The syntax is `multislice!(` *expression, (pattern [, pattern [, …]])* `)`,
639+
/// where *expression* evaluates to an `ArrayBase<S, D>` where `S: DataMut`,
640+
/// and `pattern` is one of the following:
641+
///
642+
/// * `mut expr`: creates an `ArrayViewMut`, where `expr` evaluates to a
643+
/// `&SliceInfo` instance used to slice the array.
644+
/// * `expr`: creates an `ArrayView`, where `expr` evaluates to a `&SliceInfo`
645+
/// instance used to slice the array.
646+
///
647+
/// **Note** that this macro always mutably borrows the array even if there are
648+
/// no `mut` patterns. If all you want to do is take read-only slices, you
649+
/// don't need `multislice!()`; just call
650+
/// [`.slice()`](struct.ArrayBase.html#method.slice) multiple times instead.
651+
///
652+
/// `multislice!()` follows Rust's aliasing rules:
653+
///
654+
/// * An `ArrayViewMut` and `ArrayView` cannot reference the same element.
655+
/// * Two `ArrayViewMut` cannot reference the same element.
656+
/// * Two `ArrayView` can reference the same element.
657+
///
658+
/// **Panics** at runtime if any of the aliasing rules is violated.
659+
///
660+
/// See also [*Slicing*](struct.ArrayBase.html#slicing).
661+
///
662+
/// # Examples
663+
///
664+
/// In this example, there are two overlapping read-only slices, and two
665+
/// disjoint mutable slices. Neither of the mutable slices intersects any of
666+
/// the other slices.
667+
///
668+
/// ```
669+
/// #[macro_use]
670+
/// extern crate ndarray;
671+
///
672+
/// use ndarray::prelude::*;
673+
///
674+
/// # fn main() {
675+
/// let mut arr = Array1::from_iter(0..12);
676+
/// let (a, b, c, d) = multislice!(arr, (s![0..5], mut s![6..;2], s![1..6], mut s![7..;2]));
677+
/// assert_eq!(a, array![0, 1, 2, 3, 4]);
678+
/// assert_eq!(b, array![6, 8, 10]);
679+
/// assert_eq!(c, array![1, 2, 3, 4, 5]);
680+
/// assert_eq!(d, array![7, 9, 11]);
681+
/// # }
682+
/// ```
683+
///
684+
/// These examples panic because they don't follow the aliasing rules:
685+
///
686+
/// * `ArrayViewMut` and `ArrayView` cannot reference the same element.
687+
///
688+
/// ```should_panic
689+
/// # #[macro_use] extern crate ndarray;
690+
/// # use ndarray::prelude::*;
691+
/// # fn main() {
692+
/// let mut arr = Array1::from_iter(0..12);
693+
/// multislice!(arr, (s![0..5], mut s![1..;2])); // panic!
694+
/// # }
695+
/// ```
696+
///
697+
/// * Two `ArrayViewMut` cannot reference the same element.
698+
///
699+
/// ```should_panic
700+
/// # #[macro_use] extern crate ndarray;
701+
/// # use ndarray::prelude::*;
702+
/// # fn main() {
703+
/// let mut arr = Array1::from_iter(0..12);
704+
/// multislice!(arr, (mut s![0..5], mut s![1..;2])); // panic!
705+
/// # }
706+
/// ```
707+
#[macro_export]
708+
macro_rules! multislice(
709+
(
710+
@check $view:expr,
711+
$info:expr,
712+
()
713+
) => {};
714+
// Check that $info doesn't intersect $other.
715+
(
716+
@check $view:expr,
717+
$info:expr,
718+
($other:expr,)
719+
) => {
720+
assert!(
721+
!$crate::slices_intersect(&$view.raw_dim(), $info, $other),
722+
"Slice {:?} must not intersect slice {:?}", $info, $other
723+
)
724+
};
725+
// Check that $info doesn't intersect any of the other info in the tuple.
726+
(
727+
@check $view:expr,
728+
$info:expr,
729+
($other:expr, $($more:tt)*)
730+
) => {
731+
{
732+
multislice!(@check $view, $info, ($other,));
733+
multislice!(@check $view, $info, ($($more)*));
734+
}
735+
};
736+
// Parse last slice (mutable), no trailing comma.
737+
(
738+
@parse $view:expr,
739+
($($sliced:tt)*),
740+
($($mut_info:tt)*),
741+
($($immut_info:tt)*),
742+
(mut $info:expr)
743+
) => {
744+
match $info {
745+
info => {
746+
multislice!(@check $view, info, ($($mut_info)*));
747+
multislice!(@check $view, info, ($($immut_info)*));
748+
($($sliced)* unsafe { $view.aliasing_view_mut() }.slice_move(info))
749+
}
750+
}
751+
};
752+
// Parse last slice (read-only), no trailing comma.
753+
(
754+
@parse $view:expr,
755+
($($sliced:tt)*),
756+
($($mut_info:tt)*),
757+
($($immut_info:tt)*),
758+
($info:expr)
759+
) => {
760+
match $info {
761+
info => {
762+
multislice!(@check $view, info, ($($mut_info)*));
763+
($($sliced)* unsafe { $view.aliasing_view() }.slice_move(info))
764+
}
765+
}
766+
};
767+
// Parse last slice (mutable), with trailing comma.
768+
(
769+
@parse $view:expr,
770+
($($sliced:tt)*),
771+
($($mut_info:tt)*),
772+
($($immut_info:tt)*),
773+
(mut $info:expr,)
774+
) => {
775+
match $info {
776+
info => {
777+
multislice!(@check $view, info, ($($mut_info)*));
778+
multislice!(@check $view, info, ($($immut_info)*));
779+
($($sliced)* unsafe { $view.aliasing_view_mut() }.slice_move(info))
780+
}
781+
}
782+
};
783+
// Parse last slice (read-only), with trailing comma.
784+
(
785+
@parse $view:expr,
786+
($($sliced:tt)*),
787+
($($mut_info:tt)*),
788+
($($immut_info:tt)*),
789+
($info:expr,)
790+
) => {
791+
match $info {
792+
info => {
793+
multislice!(@check $view, info, ($($mut_info)*));
794+
($($sliced)* unsafe { $view.aliasing_view() }.slice_move(info))
795+
}
796+
}
797+
};
798+
// Parse a mutable slice.
799+
(
800+
@parse $view:expr,
801+
($($sliced:tt)*),
802+
($($mut_info:tt)*),
803+
($($immut_info:tt)*),
804+
(mut $info:expr, $($t:tt)*)
805+
) => {
806+
match $info {
807+
info => {
808+
multislice!(@check $view, info, ($($mut_info)*));
809+
multislice!(@check $view, info, ($($immut_info)*));
810+
multislice!(
811+
@parse $view,
812+
($($sliced)* unsafe { $view.aliasing_view_mut() }.slice_move(info),),
813+
($($mut_info)* info,),
814+
($($immut_info)*),
815+
($($t)*)
816+
)
817+
}
818+
}
819+
};
820+
// Parse a read-only slice.
821+
(
822+
@parse $view:expr,
823+
($($sliced:tt)*),
824+
($($mut_info:tt)*),
825+
($($immut_info:tt)*),
826+
($info:expr, $($t:tt)*)
827+
) => {
828+
match $info {
829+
info => {
830+
multislice!(@check $view, info, ($($mut_info)*));
831+
multislice!(
832+
@parse $view,
833+
($($sliced)* unsafe { $view.aliasing_view() }.slice_move(info),),
834+
($($mut_info)*),
835+
($($immut_info)* info,),
836+
($($t)*)
837+
)
838+
}
839+
}
840+
};
841+
// Entry point.
842+
($arr:expr, ($($t:tt)*)) => {
843+
{
844+
let view = $crate::ArrayBase::view_mut(&mut $arr);
845+
multislice!(@parse view, (), (), (), ($($t)*))
846+
}
847+
};
848+
);

0 commit comments

Comments
 (0)