├── .gitignore ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "moveslice" 3 | description = "A one-function crate to move chunks in a slice around." 4 | license = "MPL-2.0" 5 | repository = "https://github.com/Calmynt/moveslice" 6 | readme = "README.md" 7 | keywords = ["slice", "move", "chunk", "index"] 8 | version = "2.0.1" 9 | authors = ["ENBYSS"] 10 | edition = "2018" 11 | categories = ["algorithms", "rust-patterns", "no-std"] 12 | maintenance = { status = "passively-maintained" } 13 | 14 | [dependencies] 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # moveslice 2 | 3 | [![moveslice](https://img.shields.io/crates/v/moveslice.svg)](https://crates.io/crates/moveslice) 4 | [![moveslice](https://docs.rs/moveslice/badge.svg)](https://docs.rs/crate/moveslice) 5 | 6 | This crate contains functionality to move a slice within an array around. 7 | It only uses safe functions, and acts efficiently by using the 8 | [`split_at_mut`][split-at-mut] and 9 | [`rotate_left`][rotate-left]/[`rotate_right`][rotate-right] functions. 10 | 11 | This crate also has a focus on being `no_std`, to allow this functionality 12 | in all cases where it is required. 13 | 14 | The main feature this crate provides is implementing `moveslice` functions 15 | for any and all slices/arrays. In effect, it implements it on any type that 16 | also implements the AsMut<[T]> trait. This includes slices and vectors. 17 | 18 | ## Examples: 19 | 20 | ```rust 21 | use moveslice::{Moveslice, Error}; 22 | 23 | let mut arr = [1,2,3,4,5,6,7,8,9]; 24 | 25 | // The following moves the slice 3..6 to index 1. 26 | // In effect, it moves [4,5,6] over to where [2] is. 27 | arr.moveslice(3..6, 1); 28 | assert_eq!(arr, [1,4,5,6,2,3,7,8,9]); 29 | 30 | // The following moves the slice 3..6 to index 6. 31 | // In effect, it moves [6,2,3] over to where [7] is. 32 | arr.moveslice(3..6, 6); 33 | assert_eq!(arr, [1,4,5,7,8,9,6,2,3]); 34 | 35 | // The following attempts to move the slice beyond boundaries. 36 | // The index given is 7, which exists in the array, but the 37 | // last element of the chunk will not fit (7 + 3 = 10 > 9). 38 | // Therefore, the following should fail. 39 | arr.moveslice(3..6, 7); // will panic 40 | 41 | // Panicking on failure however can prove to be not ideal. 42 | // If instead of panicking, you prefer a `Result`, use 43 | // `try_moveslice`. 44 | let res = arr.try_moveslice(3..6, 7); 45 | assert!(res.is_err()); 46 | 47 | // Moveslice also comes with its own `Error` enum, with diagnostic 48 | // information to help debugging. The line before would have triggered 49 | // an OutOfBoundsMove error. The following line would trigger the 50 | // InvalidBounds error. 51 | let res = arr.try_moveslice(9..10, 7); 52 | assert!(if let Err(Error::InvalidBounds{..}) = res {true} else {false}); 53 | 54 | // You could pass the destination as the same value as chunk.0. 55 | // However this would mean nothing is moved. 56 | // This doesn't panic, but it's a no-op. 57 | arr.moveslice(0..3, 0); 58 | ``` 59 | 60 | [split-at-mut]: https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut 61 | [rotate-left]: https://doc.rust-lang.org/std/primitive.slice.html#method.rotate_left 62 | [rotate-right]: https://doc.rust-lang.org/std/primitive.slice.html#method.rotate_right 63 | 64 | License: MPL-2.0 65 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | //! This crate contains functionality to move a slice within an array around. 3 | //! It only uses safe functions, and acts efficiently by using the 4 | //! [`split_at_mut`][split-at-mut] and 5 | //! [`rotate_left`][rotate-left]/[`rotate_right`][rotate-right] functions. 6 | //! 7 | //! This crate also has a focus on being `no_std`, to allow this functionality 8 | //! in all cases where it is required. 9 | //! 10 | //! The main feature this crate provides is implementing `moveslice` functions 11 | //! for any and all slices/arrays. In effect, it implements it on any type that 12 | //! also implements the AsMut<[T]> trait. This includes slices and vectors. 13 | //! 14 | //! # Examples: 15 | //! 16 | //! ``` 17 | //! use moveslice::{Moveslice, Error}; 18 | //! 19 | //! let mut arr = [1,2,3,4,5,6,7,8,9]; 20 | //! 21 | //! // The following moves the slice 3..6 to index 1. 22 | //! // In effect, it moves [4,5,6] over to where [2] is. 23 | //! arr.moveslice(3..6, 1); 24 | //! assert_eq!(arr, [1,4,5,6,2,3,7,8,9]); 25 | //! 26 | //! // The following moves the slice 3..6 to index 6. 27 | //! // In effect, it moves [6,2,3] over to where [7] is. 28 | //! arr.moveslice(3..6, 6); 29 | //! assert_eq!(arr, [1,4,5,7,8,9,6,2,3]); 30 | //! 31 | //! // The following attempts to move the slice beyond boundaries. 32 | //! // The index given is 7, which exists in the array, but the 33 | //! // last element of the chunk will not fit (7 + 3 = 10 > 9). 34 | //! // Therefore, the following should fail. 35 | //! # #[should_panic] 36 | //! # fn main() { 37 | //! # let mut arr = [1,2,3,4,5,6,7,8,9]; 38 | //! arr.moveslice(3..6, 7); // will panic 39 | //! # } 40 | //! 41 | //! // Panicking on failure however can prove to be not ideal. 42 | //! // If instead of panicking, you prefer a `Result`, use 43 | //! // `try_moveslice`. 44 | //! let res = arr.try_moveslice(3..6, 7); 45 | //! assert!(res.is_err()); 46 | //! 47 | //! // Moveslice also comes with its own `Error` enum, with diagnostic 48 | //! // information to help debugging. The line before would have triggered 49 | //! // an OutOfBoundsMove error. The following line would trigger the 50 | //! // InvalidBounds error. 51 | //! let res = arr.try_moveslice(9..10, 7); 52 | //! assert!(if let Err(Error::InvalidBounds{..}) = res {true} else {false}); 53 | //! 54 | //! // You could pass the destination as the same value as chunk.0. 55 | //! // However this would mean nothing is moved. 56 | //! // This doesn't panic, but it's a no-op. 57 | //! arr.moveslice(0..3, 0); 58 | //! ``` 59 | //! 60 | //! [split-at-mut]: https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut 61 | //! [rotate-left]: https://doc.rust-lang.org/std/primitive.slice.html#method.rotate_left 62 | //! [rotate-right]: https://doc.rust-lang.org/std/primitive.slice.html#method.rotate_right 63 | 64 | use core::ops::Bound::*; 65 | use core::ops::RangeBounds; 66 | 67 | /// This Error enum has a single variant, which is used to return additional information about 68 | /// the out of bounds error, to help diagnostics. 69 | /// 70 | /// Is used/returned by `try_moveslice`. 71 | #[derive(Debug)] 72 | pub enum Error { 73 | /// This error signifies an out of bounds error. 74 | /// It also contains the length of the slice, and 75 | /// the supposed location of where the chunk would have been. 76 | /// 77 | /// For example: 78 | /// `OutOfBoundsMove {len: 10, dest: (8,11)}` 79 | OutOfBoundsMove { 80 | /// The length of the array/slice being modified. 81 | len: usize, 82 | /// The location of where the chunk would have ended up. 83 | dest: (usize, usize) 84 | }, 85 | 86 | /// This error signifies an invalid bounds error. 87 | /// If the bounds passed are already out of bounds, this 88 | /// error is returned instead. This is to differentiate 89 | /// between the two out-of-bounds cases. 90 | InvalidBounds { 91 | // The length of the array/slice being modified. 92 | len: usize, 93 | // The effective bounds passed in. 94 | bounds: (usize, usize) 95 | } 96 | } 97 | 98 | /// A trait declaring the `moveslice` and `try_moveslice` functions. 99 | /// Used to implement the functions on all slices. 100 | pub trait Moveslice { 101 | /// Specifies the type of the destination index. 102 | type Target; 103 | 104 | /// Specifies the errors being returned. 105 | type Err; 106 | 107 | /// Moves a slice within an array/slice around. 108 | /// 109 | /// - `bounds` - specifies the range of where the subslice is. Examples: 3..5, 5..=8 110 | /// - `destination` - specifies where the subslice should be moved to. 111 | fn moveslice(&mut self, bounds: R, destination: Self::Target) 112 | where R: RangeBounds; 113 | 114 | /// Similar to `moveslice`, except it does not panic, returning a `Result` instead. 115 | fn try_moveslice(&mut self, bounds: R, destination: Self::Target) -> Result<(), Self::Err> 116 | where R: RangeBounds; 117 | } 118 | 119 | /// Implements the moveslice functions on all slices. 120 | impl Moveslice for A where A: AsMut<[T]> { 121 | type Target = usize; 122 | type Err = Error; 123 | 124 | fn moveslice(&mut self, bounds: R, destination: Self::Target) 125 | where R: RangeBounds 126 | { 127 | let res = self.try_moveslice(bounds, destination); 128 | if let Err(Error::OutOfBoundsMove{len, dest: (x,y)}) = res { 129 | panic!("Movement goes beyond bounds. [len = {}, destination = {}..{}]", len, x, y); 130 | } 131 | else if let Err(Error::InvalidBounds{len, bounds: (x,y)}) = res { 132 | panic!("Bounds passed go beyond slice length. [len = {}, bounds = {}..{}]", len, x, y); 133 | } 134 | } 135 | 136 | fn try_moveslice(&mut self, bounds: R, destination: Self::Target) -> Result<(), Self::Err> 137 | where R: RangeBounds 138 | { 139 | let slice = self.as_mut(); 140 | let startbound = bounds.start_bound(); 141 | let endbound = bounds.end_bound(); 142 | let x = if let Included(x) = startbound {*x} else {0}; 143 | let y = if let Excluded(x) = endbound {*x} 144 | else if let Included(x) = endbound {x+1} 145 | else {slice.len()}; 146 | let chunk = (x,y); 147 | 148 | if chunk.0 > slice.len() || chunk.1 > slice.len() { 149 | return Err(Error::InvalidBounds { 150 | len: slice.len(), 151 | bounds: chunk 152 | }); 153 | } 154 | 155 | if destination > chunk.0 { 156 | let chunksize = chunk.1 - chunk.0; 157 | let index1 = chunk.0; 158 | let index2 = destination + chunksize - index1; 159 | 160 | let (_, mid) = slice.split_at_mut(index1); 161 | 162 | let mid = if index2 <= mid.len() { 163 | mid.split_at_mut(index2).0 164 | } else { 165 | return Err(Error::OutOfBoundsMove { 166 | len: slice.len(), 167 | dest: (destination, destination + chunksize), 168 | }); 169 | }; 170 | 171 | mid.rotate_left(chunk.1-chunk.0); 172 | } else if destination < chunk.0 { 173 | let index1 = destination; 174 | let index2 = chunk.1 - destination; 175 | 176 | let (_, mid) = slice.split_at_mut(index1); 177 | 178 | let mid = mid.split_at_mut(index2).0; 179 | 180 | mid.rotate_right(chunk.1-chunk.0); 181 | } 182 | 183 | Ok(()) 184 | } 185 | } 186 | --------------------------------------------------------------------------------