better_comprehension
Collection comprehension and Iterator comprehension in Rust. And it provides a better experience in Rust.
This library aims to be a good alternative to all comprehension libraries, for the libraries you encounter when searching "comprehension" on crate.io, we have already done: Fully covered libraries:
Partially covered libraries:
-
Does not provide a unified macro that distinguishes by mapping expression (like real Python comprehensions)
(No plans to support this, as this library already provides all collection types in the Rust standard library)
-
Does not support while loop
-
Does not support let-else variable binding
(No plans to support this, as it overly complicates the for-in part, and these things can be completely solved in the mapping return block)
Collection Comprehensions
You can completely treat collection comprehension macros as sugar for for loop
(In fact, these macros are implemented using for loop)
So you'll see many familiar syntaxes
However, they will be more ergonomic and easier to read and use
Simple Example
use vector;
let vec_1 = vec!;
let vec_2 = vec!;
// Ownership consuming iteration
// (just pass the single identifier)
// x's type is &String
let vec: = vector!;
// println!("{:?}", vec_1); // borrow of moved value
assert_eq!;
// Ownership preserving iteration
// (pass &collection or collection.iter().other_method())
// x's type is &String
let vec: = vector!;
// equivalent writing
// let vec: Vec<String> = vector![x.clone() for x in &vec_2];
println!; // vec_2 is alive
assert_eq!;
if in comprehension
for pattern in collection if ... will be translated to
for pattern in collection
if conditions as filtering conditions
Where conditions is any expression that returns a bool Only when the expression returns true, it will be mapped
use linked_list;
use LinkedList;
// i's type is i32
let linked_list = linked_list!;
assert_eq!;
use linked_list;
use LinkedList;
let judge_function = ;
// i's type is i32
let linked_list = linked_list!;
assert_eq!;
if let expression
use vector;
let vec_1 = vec!;
let vec = vector!;
assert_eq!;
Return different values based on conditions
use b_tree_set;
use BTreeSet;
let b_tree_set = b_tree_set!;
assert_eq!;
Use pattern matching
use vec_deque;
use VecDeque;
let people = ;
// name's type is &String
let vec_deque: = vec_deque!;
println!; // people is alive
assert_eq!;
Nested Comprehensions
Like Python's comprehensions, this library's for loop is read from top to bottom.
use binary_heap;
use BinaryHeap;
let binary_heap = binary_heap!;
assert_eq!;
use vector;
// You can use the upper variable in the lower loop
let vec = vector!;
assert_eq!;
Execute code in block before returning
This is a very powerful feature, you can execute any code before returning but it will reduce readability, please use it with caution
use vector;
let vec_1 = vec!;
let vec_2 = vec!;
let vec = vector!;
// println!("{:?}", vec_1); // borrow of moved value
println!; // vec_2 is alive
assert_eq!;
description of ergonomic
Please note, in Rust, for loop consumes ownership. So usually, for multi-layer loops, if you want the original collection to be consumed, you should write it like this:
use vector;
let vec_1 = vec!;
let vec_2 = vec!;
let vec_3 = vec!;
let vec = ;
// println!("{:?}", vec_1); // borrow of moved value
println!; // work well
// println!("{:?}", vec_3); // borrow of moved value
In this library, you don't need to do this, the macros will automatically handle these problems for you. You only need to do two things:
- For the collection you want to keep ownership, add
.iter()or use& - Directly pass the variable name of the collection you want to consume
The rest will be automatically handled in the macro.
use vector;
let vec_1 = vec!;
let vec_2 = vec!;
let vec_3 = vec!;
let vec = vector!;
// println!("{:?}", vec_1); // borrow of moved value
println!; // work well
// println!("{:?}", vec_3); // borrow of moved value
Key-value collection types
Also, this library supports key-value collection types, HashMap, BTreeMap And supports three key-value separators "=>" ":" ","
use hash_map;
use HashMap;
let vec_key = vec!;
let vec_value = vec!;
let hash_map = hash_map! ;
println!; // vec_key is alive
// println!("{:?}", vec_value); // borrow of moved value
assert_eq!;
Some details
vector! : push() to add elements
binary_heap! : push() to add elements
vec_deque! : push_back() to add elements
linked_list! : push_back() to add elements
hash_set! : insert() to add elements
hash_map! : insert() to add key-value pairs
b_tree_map! : insert() to add key-value pairs
b_tree_set! : insert() to add elements
Iterator Comprehensions
This library also supports iterator comprehensions, but as the author, I do not recommend using them, the reasons are as follows:
- In the collection comprehension, we also use references to derive, as long as we do not consume the original collection, we can achieve the same thing
- The cost of getting a reference copy is not large
- Because rust does not have a
yieldkeyword, the implementation of iterator comprehension is complex, which leads to the inability to use many collection comprehension features
The iterator comprehension is based on references, so it always does not consume ownership However, to ensure the correctness of the iterator comprehension, only two iterable objects are allowed to be passed in:
- Single identifier (not followed by any method calls)
- Range expression (such as: 1..=3 or 1..x )
use iterator_ref;
let vec_1 = ;
let vec_2 = ;
let mut result3 = iterator_ref!;
// still alive
println!;
println!;
for _ in 0..=9
/*
Some(("123", "ABC"))
Some(("123", "DEF"))
Some(("123", "GHI"))
Some(("123", "ABC"))
Some(("123", "DEF"))
Some(("123", "GHI"))
Some(("789", "DEF"))
Some(("789", "DEF"))
None
None
*/
The above writing is equivalent to the following writing
let vec_1 = ;
let vec_2 = ;
let mut result3 = ;
This implementation makes the following features in collection comprehension unavailable in iterator comprehension:
- if let expression
Differences
-
Ownership consumption:
-
Collection comprehension:
- Using & or .iter() does not consume ownership
- Directly passing the variable name consumes ownership
-
Iterator comprehension:
- Always does not consume ownership, but only allows passing in a single identifier and range expression that does not follow any method calls
-
Differences in features:
-
if let expression
- Collection comprehension: supported
- Iterator comprehension: not supported
Some practical examples
use vector;
use ;
// Create a 3x3 matrix
let matrix = vector!;
// Transpose the matrix
let transposed = vector!;
// matrix is alive
assert_eq!;
assert_eq!;
use ;
use ;
let students_data = ;
// use for loop
let math_scores: = ;
// ↓ Equivalent to ↓
// use comprehension!
let math_scores: = hash_map!;
assert_eq!;
// use for loop
let high_scores = ;
// ↓ Equivalent to ↓
// use comprehension!
let high_scores = b_tree_map!;
assert_eq!;