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