# Indices [github](https://github.com/mcmah309/indices) [crates.io](https://crates.io/crates/indices) [docs.rs](https://docs.rs/indices) [build status](https://github.com/mcmah309/indices/actions?query=branch%3Amaster) Indices provides macros and methods for **safely** retrieving **multiple mutable elements** from **a mutable slice**, addressing scenarios where slice elements would typically require `RefCell` or `Cell` (interior mutability approach). e.g. ```rust let (four, one, two) = indices!(slice, 4, 1, 2); ``` Which expands to ```rust #[inline(always)] fn func(slice: &mut [T], one: usize, two: usize, three: usize,) -> (&mut T, &mut T, &mut T) { if one == two || one == three || two == three { panic!("Duplicate indices are not allowed."); } let slice_len = slice.len(); if one >= slice_len || two >= slice_len || three >= slice_len { panic!("Index out of bounds."); } let ptr = slice.as_mut_ptr(); unsafe { (&mut *ptr.add(one), &mut *ptr.add(two), &mut *ptr.add(three)) } } let (four, one, two) = func(slice, 4, 1, 2); ``` Which will be optimized by the rust compiler to essentially the following pseudo code ```rust if 4 >= slice.len() { panic!("Index out of bounds."); } let (four, one, two) = (&mut slice[4], &mut slice[1], &mut slice[2]) ``` The above code is safe, correct, and more performant than using `RefCell` or `Cell`. `indices!` follows the above expansion pattern for up to 4 requested indices. At which point, the macro will switch to a more optimized approach for many requested indices. There is also `try_indices!`, `indices_ordered!`, and `try_indices_ordered!`. ### Examples
Macro Example All macros are zero allocation and allow retrieving a variable number of indices at runtime. Prefer macros when the number of indices are known at compile time. e.g. ```rust fn main() { struct Person { first: String, last: String, } let mut data = [ Person { first: "John".to_string(), last: "Doe".to_string() }, Person { first: "Jane".to_string(), last: "Smith".to_string() }, Person { first: "Alice".to_string(), last: "Johnson".to_string() }, Person { first: "Bob".to_string(), last: "Brown".to_string() }, Person { first: "Charlie".to_string(), last: "White".to_string() }, ]; fn modify(data_slice: &mut [Person], index: usize){ let (four, func_provided, three) = indices!(data_slice, 4, index, 3); four.last = "Black".to_string(); func_provided.first = "Jack".to_string(); three.last = "Jones".to_string(); } let slice = data.as_mut_slice(); modify(slice, 1); assert_eq!(data[4].last, "Black"); assert_eq!(data[1].first, "Jack"); assert_eq!(data[3].last, "Jones"); } ```
Method Example Methods allow for more dynamic runtime retrieval when the number of indices is unknown at compile time. e.g. ```rust fn main() { struct Node { index: usize, visited: usize, edges: Vec, message: String, } let mut graph = vec![ Node { index: 0, visited: usize::MAX, edges: vec![1, 2], message: String::new(), }, Node { index: 1, visited: usize::MAX, edges: vec![0, 2], message: String::new(), }, Node { index: 2, visited: usize::MAX, edges: vec![3], message: String::new(), }, Node { index: 4, visited: usize::MAX, edges: vec![1], message: String::new(), }, ]; fn traverse_graph(graph: &mut [Node], current: usize, start: usize) -> bool { if current == start { return true; } let edges = graph[current].edges.clone(); let [mut current_node, mut edge_nodes] = indices_slices(graph, [&[current], &edges]); for edge_node in edge_nodes.iter_mut() { current_node[0].visited = current; edge_node.message.push_str(&format!( "This is Node `{}` Came from Node `{}`.", edge_node.index, current_node[0].visited )); } for edge in edges { if traverse_graph(graph, edge, start) { return true; } } return false; } traverse_graph(&mut *graph, 2, 0); let answers = [ "This is Node `0` Came from Node `1`.", "This is Node `1` Came from Node `3`.", "This is Node `2` Came from Node `1`.", "This is Node `4` Came from Node `2`.", ]; for (index, node) in graph.iter().enumerate() { assert_eq!(&node.message, answers[index]); } } ```