|
| 1 | +//! # better_comprehension |
| 2 | +//! [中文README](https://github.com/Natural-selection1/better-comprehension-in-rust/blob/master/README-CN.md) |
| 3 | +//! |
| 4 | +//! Collection comprehension and Iterator comprehension in Rust. And it provides a better experience in Rust. |
| 5 | +//! |
| 6 | +//! # Usage |
| 7 | +//! |
| 8 | +//! The syntax is derived from [Python's comprehension](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions). |
| 9 | +//! |
| 10 | +//! This library provides macros for all collection types in the Rust standard library and an Iterator based on references. |
| 11 | +//! |
| 12 | +//! --- |
| 13 | +//! simple example |
| 14 | +//! ```rust |
| 15 | +//! use better_comprehension::vector; |
| 16 | +//! let vec_1 = vec!["AB".to_string(), "CD".to_string()]; |
| 17 | +//! let vec: Vec<String> = vector![x.clone() for x in vec_1]; |
| 18 | +//! assert_eq!(vec, vec!["AB".to_string(), "CD".to_string()]); |
| 19 | +//! ``` |
| 20 | +//! --- |
| 21 | +//! You can also use patterns in it |
| 22 | +//! ```rust |
| 23 | +//! use better_comprehension::vec_deque; |
| 24 | +//! use std::collections::VecDeque; |
| 25 | +//! struct Person { |
| 26 | +//! name: String, |
| 27 | +//! age: i32, |
| 28 | +//! } |
| 29 | +//! let people = [Person { name: "Joe".to_string(), age: 20 }, |
| 30 | +//! Person { name: "Bob".to_string(), age: 25 }]; |
| 31 | +//! let vec_deque = vec_deque![ name.clone() |
| 32 | +//! for Person { name, ..} in people]; |
| 33 | +//! assert_eq!(vec_deque, |
| 34 | +//! VecDeque::from(["Joe".to_string(), |
| 35 | +//! "Bob".to_string()])); |
| 36 | +//! ``` |
| 37 | +//! --- |
| 38 | +//! filtering values before comprehension |
| 39 | +//! ```rust |
| 40 | +//! use better_comprehension::linked_list; |
| 41 | +//! use std::collections::LinkedList; |
| 42 | +//! let linked_list = linked_list![ i*2 for i in 1..=3 if i != 2 ]; |
| 43 | +//! assert_eq!(linked_list, LinkedList::from([2, 6])); |
| 44 | +//! ``` |
| 45 | +//! --- |
| 46 | +//! return different values based on conditions |
| 47 | +//! ```rust |
| 48 | +//! use better_comprehension::b_tree_set; |
| 49 | +//! use std::collections::BTreeSet; |
| 50 | +//! let b_tree_set = b_tree_set!{ |
| 51 | +//! i if i-1 == 0 else i+10 |
| 52 | +//! for i in 1..=3 if i != 2 |
| 53 | +//! }; |
| 54 | +//! assert_eq!(b_tree_set, BTreeSet::from([1, 13])); |
| 55 | +//! ``` |
| 56 | +//! --- |
| 57 | +//! nested comprehension |
| 58 | +//! ```rust |
| 59 | +//! use better_comprehension::binary_heap; |
| 60 | +//! use std::collections::BinaryHeap; |
| 61 | +//! let binary_heap = binary_heap![ |
| 62 | +//! i if (i-1 == 0 || j -2 == 0) else i+10 |
| 63 | +//! for i in 1..=3 if i != 2 |
| 64 | +//! for j in 1..=3 if j+i != 4]; |
| 65 | +//! assert_eq!(binary_heap.into_sorted_vec(), vec![1, 1, 3, 13]); |
| 66 | +//! ``` |
| 67 | +//! --- |
| 68 | +//! the reading order of the for loop in this library is from top to bottom, just like Python's comprehension. |
| 69 | +//! ```rust |
| 70 | +//! use better_comprehension::vector; |
| 71 | +//! let vec = vector![ |
| 72 | +//! (top,bottom) |
| 73 | +//! for top in 1..=3 if top != 2 |
| 74 | +//! for bottom in 4..=6 if bottom+top != 4]; |
| 75 | +//! assert_eq!(vec, vec![(1, 4), (1, 5), (1, 6), |
| 76 | +//! (3, 4), (3, 5), (3, 6)]); |
| 77 | +//! ``` |
| 78 | +//! --- |
| 79 | +//! |
| 80 | +//! Note that in Rust, for loops consume ownership. |
| 81 | +//! So typically, for nested loops, if you want the original container to be consumed, you should write it like this: |
| 82 | +//! |
| 83 | +//! ```rust |
| 84 | +//! use better_comprehension::vector; |
| 85 | +//! let vec_1 = vec!["ABC".to_string(), "DEF".to_string()]; |
| 86 | +//! let vec_2 = vec!["abc".to_string(), "def".to_string()]; |
| 87 | +//! let vec_3 = vec![123, 456]; |
| 88 | +//! let vec = { |
| 89 | +//! // shadow the variable you want to consume |
| 90 | +//! let vec_1 = vec_1; |
| 91 | +//! let vec_3 = vec_3; |
| 92 | +//! |
| 93 | +//! let mut vec = vec![]; |
| 94 | +//! for i in vec_1.iter() { |
| 95 | +//! if i == "ABC" { |
| 96 | +//! // In the inner loop, you must use iter(), |
| 97 | +//! // otherwise ownership will be transferred for the first time |
| 98 | +//! for j in vec_2.iter() { |
| 99 | +//! if j == "abc" { |
| 100 | +//! // If you do not use iter(), |
| 101 | +//! // then the ownership of vec_3 |
| 102 | +//! // will be transferred for the first time |
| 103 | +//! for k in vec_3.iter() { |
| 104 | +//! if k == &123 { |
| 105 | +//! // Only use clone when necessary |
| 106 | +//! // to avoid unnecessary resource waste |
| 107 | +//! vec.push((i.clone(), j.clone(), *k)); |
| 108 | +//! } |
| 109 | +//! } |
| 110 | +//! } |
| 111 | +//! } |
| 112 | +//! } |
| 113 | +//! } |
| 114 | +//! vec |
| 115 | +//! }; |
| 116 | +//! // println!("{:?}", vec_1); // borrow of moved value |
| 117 | +//! println!("{:?}", vec_2); // work well |
| 118 | +//! // println!("{:?}", vec_3); // borrow of moved value |
| 119 | +//! ``` |
| 120 | +//! --- |
| 121 | +//! But in this library, you don't need to do this, |
| 122 | +//! |
| 123 | +//! the provided macros will automatically handle these problems for you. |
| 124 | +//! |
| 125 | +//! You only need to add .iter() or use & before the variable you want to keep ownership, |
| 126 | +//! |
| 127 | +//! the rest will be automatically handled in the macro. |
| 128 | +//! ```rust |
| 129 | +//! use better_comprehension::vector; |
| 130 | +//! let vec_1 = vec!["ABC".to_string(), "DEF".to_string()]; |
| 131 | +//! let vec_2 = vec!["abc".to_string(), "def".to_string()]; |
| 132 | +//! let vec_3 = vec![123, 456]; |
| 133 | +//! let vec = vector![ |
| 134 | +//! (i.clone(),j.clone(),*k) |
| 135 | +//! for i in vec_1 if i == "ABC" |
| 136 | +//! for j in vec_2.iter() if j == "abc" |
| 137 | +//! // for j in &vec_2 if j == "abc" // this is also reasonable |
| 138 | +//! for k in vec_3 if k == &123 |
| 139 | +//! ]; |
| 140 | +//! // println!("{:?}", vec_1); // borrow of moved value |
| 141 | +//! println!("{:?}", vec_2); // work well |
| 142 | +//! // println!("{:?}", vec_3); // borrow of moved value |
| 143 | +//! ``` |
| 144 | +//! --- |
| 145 | +//! This library also supports key-value collection types, HashMap, BTreeMap |
| 146 | +//! ```rust |
| 147 | +//! use better_comprehension::hash_map; |
| 148 | +//! use std::collections::HashMap; |
| 149 | +//! let vec_key = vec!["key_1".to_string(), "key_2".to_string(), "key_3".to_string()]; |
| 150 | +//! let vec_value = [1, 2, 3]; |
| 151 | +//! let hash_map = hash_map!{ |
| 152 | +//! // the following three key-value pair separators are supported |
| 153 | +//! key.clone() : *value |
| 154 | +//! // key.clone() => *value |
| 155 | +//! // key.clone() , *value |
| 156 | +//! for key in vec_key |
| 157 | +//! for value in vec_value |
| 158 | +//! }; |
| 159 | +//! assert_eq!( |
| 160 | +//! hash_map, |
| 161 | +//! HashMap::from([ |
| 162 | +//! ("key_1".to_string(), 3), |
| 163 | +//! ("key_2".to_string(), 3), |
| 164 | +//! ("key_3".to_string(), 3) |
| 165 | +//! ]) |
| 166 | +//! ); |
| 167 | +//! ``` |
| 168 | +//! --- |
| 169 | +//! Iterator comprehension is also supported,but unlike the collection comprehension above, |
| 170 | +//! |
| 171 | +//! this iterator comprehension is based on references,so it will not consume ownership. |
| 172 | +//! ```rust |
| 173 | +//! use better_comprehension::iterator_ref; |
| 174 | +//! let vec_1 = ["123".to_string(), "456".to_string(), "789".to_string()]; |
| 175 | +//! let vec_2 = ["ABC".to_string(), "DEF".to_string(), "GHI".to_string()]; |
| 176 | +
|
| 177 | +//! let mut result3 = iterator_ref![ |
| 178 | +//! (x.clone(), y.clone()) if x.contains("1") else (y.clone(), x.clone()) |
| 179 | +//! for x in vec_1 if x.contains("1") || x.contains("7") |
| 180 | +//! for i in 1..=2 |
| 181 | +//! for y in vec_2 if y.contains("D") || x.contains("3")]; |
| 182 | +
|
| 183 | +//! for _ in 0..=9 { |
| 184 | +//! println!("{:?}", result3.next()); |
| 185 | +//! } |
| 186 | +//! /* |
| 187 | +//! Some(("123", "ABC")) |
| 188 | +//! Some(("123", "DEF")) |
| 189 | +//! Some(("123", "GHI")) |
| 190 | +//! Some(("123", "ABC")) |
| 191 | +//! Some(("123", "DEF")) |
| 192 | +//! Some(("123", "GHI")) |
| 193 | +//! Some(("789", "DEF")) |
| 194 | +//! Some(("789", "DEF")) |
| 195 | +//! None |
| 196 | +//! None |
| 197 | +//! */ |
| 198 | +//! ``` |
| 199 | +
|
| 200 | +//! The above writing is equivalent to the following writing |
| 201 | +//!```rust |
| 202 | +//!let vec_1 = ["123".to_string(), "456".to_string(), "789".to_string()]; |
| 203 | +//!let vec_2 = ["ABC".to_string(), "DEF".to_string(), "GHI".to_string()]; |
| 204 | +//!let mut result3 = { |
| 205 | +//! let vec_2 = vec_2.iter().collect::<Vec<_>>(); |
| 206 | +//! let vec_1 = vec_1.iter().collect::<Vec<_>>(); |
| 207 | +//! (vec_1).into_iter().filter_map(move |x| { |
| 208 | +//! (x.contains("1") || x.contains("7")).then(|| { |
| 209 | +//! let vec_2 = vec_2.clone(); |
| 210 | +//! (1..=2).into_iter().filter_map(move |_| { |
| 211 | +//! (true).then(|| { |
| 212 | +//! let vec_2 = vec_2.clone(); |
| 213 | +//! (vec_2).into_iter().filter_map(move |y| { |
| 214 | +//! (y.contains("D") || x.contains("3")).then(|| { |
| 215 | +//! if x.contains("1") { |
| 216 | +//! (x.clone(), y.clone()) |
| 217 | +//! } else { |
| 218 | +//! (y.clone(), x.clone()) |
| 219 | +//! } |
| 220 | +//! }) |
| 221 | +//! }) |
| 222 | +//! }) |
| 223 | +//! }) |
| 224 | +//! }) |
| 225 | +//! }) |
| 226 | +//! .flatten() |
| 227 | +//! .flatten() |
| 228 | +//!}; |
| 229 | +//!``` |
| 230 | +//! # some details |
| 231 | +//! |
| 232 | +//! vector! : push() to add elements |
| 233 | +//! |
| 234 | +//! vec_deque! : push_back() to add elements |
| 235 | +//! |
| 236 | +//! linked_list! : push_back() to add elements |
| 237 | +//! |
| 238 | +//! binary_heap! : push() to add elements |
| 239 | +//! |
| 240 | +//! hash_set! : insert() to add elements |
| 241 | +//! |
| 242 | +//! b_tree_set! : insert() to add elements |
| 243 | +//! |
| 244 | +//! hash_map! : insert() to add key-value pairs |
| 245 | +//! |
| 246 | +//! b_tree_map! : insert() to add key-value pairs |
| 247 | +
|
1 | 248 | use proc_macro::TokenStream as TS; |
2 | 249 |
|
3 | 250 | mod eager_evaluation; |
|
0 commit comments