上QQ阅读APP看书,第一时间看更新
How to do it...
In four easy steps, we'll be able to filter and transform collections in Rust:
- In order to use an iterator, you have to retrieve it first! Let's do that and implement a test that quickly shows how an iterator works on a regular Rust Vec<T>:
#[test]
fn getting_the_iterator() {
let v = vec![10, 10, 10];
let mut iter = v.iter();
assert_eq!(iter.next(), Some(&10));
assert_eq!(iter.next(), Some(&10));
assert_eq!(iter.next(), Some(&10));
assert_eq!(iter.next(), None);
for i in v {
assert_eq!(i, 10);
}
}
- With one test added, let's explore the notion of iterator functions further. They are compose able and let you perform multiple steps in a single iteration (think of adding more things to a single for loop). Additionally, the outcome's type can be completely different from what you started with! Here is another test to add to the project that performs some data transformations:
fn count_files(path: &String) -> usize {
path.len()
}
#[test]
fn data_transformations() {
let v = vec![10, 10, 10];
let hexed = v.iter().map(|i| format!("{:x}", i));
assert_eq!(
hexed.collect::<Vec<String>>(),
vec!["a".to_string(), "a".to_string(), "a".to_string()]
);
assert_eq!(v.iter().fold(0, |p, c| p + c), 30);
let dirs = vec![
"/home/alice".to_string(),
"/home/bob".to_string(),
"/home/carl".to_string(),
"/home/debra".to_string(),
];
let file_counter = dirs.iter().map(count_files);
let dir_file_counts: Vec<(&String, usize)> =
dirs.iter().zip(file_counter).collect();
assert_eq!(
dir_file_counts,
vec![
(&"/home/alice".to_string(), 11),
(&"/home/bob".to_string(), 9),
(&"/home/carl".to_string(), 10),
(&"/home/debra".to_string(), 11)
]
)
}
- As the final step, let's also look at some filtering and splitting. These have proven to be the most useful in our personal experience—it removes a lot of code verbosity. Here is some code:
#[test]
fn data_filtering() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
assert!(data.iter().filter(|&n| n % 2 == 0).all(|&n| n % 2
== 0));
assert_eq!(data.iter().find(|&&n| n == 5), Some(&5));
assert_eq!(data.iter().find(|&&n| n == 0), None);
assert_eq!(data.iter().position(|&n| n == 5), Some(4));
assert_eq!(data.iter().skip(1).next(), Some(&2));
let mut data_iter = data.iter().take(2);
assert_eq!(data_iter.next(), Some(&1));
assert_eq!(data_iter.next(), Some(&2));
assert_eq!(data_iter.next(), None);
let (validation, train): (Vec<i32>, Vec<i32>) = data
.iter()
.partition(|&_| (rand::random::<f32>() % 1.0) > 0.8);
assert!(train.len() > validation.len());
}
- As always, we want to see the examples working! Run cargo test to do just that:
$ cargo test
Compiling libc v0.2.50
Compiling rand_core v0.4.0
Compiling iteration v0.1.0 (Rust-Cookbook/Chapter02/iteration)
Compiling rand_core v0.3.1
Compiling rand v0.5.6
Finished dev [unoptimized + debuginfo] target(s) in 5.44s
Running target/debug/deps/iteration-a23e5d58a97c9435
running 3 tests
test tests::data_transformations ... ok
test tests::getting_the_iterator ... ok
test tests::data_filtering ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Doc-tests iteration
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Do you want to know more? Let's see how it works.