• Rust7.1 Functional Language Features Iterators and Closures


    Rust学习笔记

    Rust编程语言入门教程课程笔记

    参考教材: The Rust Programming Language (by Steve Klabnik and Carol Nichols, with contributions from the Rust Community)

    Lecture 13: Functional Language Features Iterators and Closures

    use std::thread;
    use std::time::Duration;
    
    //Fn Traits : Fn, FnMut, FnOnce
    
    struct Cacher<T>
        where T: Fn(u32) -> u32
    {
        calculation: T,
        value: Option<u32>,
    }
    
    impl<T> Cacher<T>
    where T: 
        Fn(u32) -> u32,
        {
            fn new(calculation: T) -> Cacher<T> {
                Cacher {
                    calculation,
                    value: None,
                }
            }
    
            fn value(&mut self, arg: u32) -> u32 {
                match self.value {
                    Some(v) => v,
                    None => {
                        let v = (self.calculation)(arg);
                        self.value = Some(v);
                        v
                    }
                }
            }
        }
    
    #[derive(PartialEq, Debug)]
    pub struct Shoe {
        size: u32,
        style: String,
    }
    
    pub fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
        shoes.into_iter()
            .filter(|s| s.size == shoe_size)
            .collect()
    }
    
    fn main() {
        let simulated_user_specified_value = 10;
        let simulated_random_number = 7;
    
        generate_workout(
            simulated_user_specified_value,
            simulated_random_number
        );
    
        let x = 4;
        let equal_to_x = |z| z == x; //closure can use variable from the scope in which they’re defined
        // fn equal_to_x(z: i32) -> bool { z == x } //function can't use variable from the scope in which they’re defined
        let y = 4;
        assert!(equal_to_x(y));
    
        //capturing the environment with closures
        // 1. FnOnce: consumes the variables it captures from its enclosing scope, known as the closure’s environment.
        // 2. FnMut: can change the environment because it mutably borrows values.
        // 3. Fn: borrows values from the environment immutably.
        // Fn > FnMut > FnOnce
    
        // move the ownership of the environment variables into the closure 
        let x = vec![1, 2, 3];
        let equal_to_x = move |z| z == x; //x is moved into the closure and so the closure owns x
        // println!("can't use x here: {:?}", x); //error: value borrowed here after move
        let y = vec![1, 2, 3];
        assert!(equal_to_x(y));
    
        //iterators
        let v1 = vec![1, 2, 3];
        let v1_iter = v1.iter(); //v1_iter is immutable
        for val in v1_iter {
            println!("Got: {}", val);
        }
    
        //iterator trait
        // pub trait Iterator {
        //     type Item;
        //     fn next(&mut self) -> Option;
        // }
    
    }
    
    fn _simulated_expensive_calculation(intensity: u32) -> u32 {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        intensity
    }
    
    //compare function and closure
    // 1. function: fn add_one_v1(x: u32) -> u32 { x + 1 }
    // 2. closure: let add_one_v2 = |x: u32| -> u32 { x + 1 };
    // 3. closure: let add_one_v3 = |x| { x + 1 };
    // 4. closure: let add_one_v4 = |x| x + 1;
    
    // The compiler will infer only one concrete type for each parameter and for the return value
    // of closures, because the compiler will analyze the body of the closure to determine the types
    // of each parameter and the return type. If there are multiple ways to infer the types, the
    // compiler will error. This code will not compile because the compiler can’t determine the
    // types of the closure’s parameters or the return type of the closure:
    
    // let example_closure = |x| x;
    // let s = example_closure(String::from("hello"));
    // let n = example_closure(5);
    
    fn generate_workout(intensity: u32, random_num: u32) {
    
        // let expensive_closure = |num: u32| -> u32{  //the compiler can infer the type of the parameter and the return type
        //     println!("calculating slowly...");
        //     thread::sleep(Duration::from_secs(2));
        //     num
        // };
    
        let mut expensive_closure = Cacher::new(|num| {
            println!("calculating slowly...");
            thread::sleep(Duration::from_secs(2));
            num
        });
    
        // let expensive_result = simulated_expensive_calculation(intensity);
        if intensity < 25 {
            // println!(
            //     "Today, do {} pushups!",
            //     simulated_expensive_calculation(intensity)
            // );
            // println!(
            //     "Next, do {} situps!",
            //     simulated_expensive_calculation(intensity)
            // );
    
            //optimization 1
            // println!(
            //     "Today, do {} pushups!",
            //     expensive_result
            // );
            // println!(
            //     "Next, do {} situps!",
            //     expensive_result
            // );
            println!(
                "Today, do {} pushups!",
                expensive_closure.value(intensity)
            );
            println!(
                "Next, do {} situps!",
                expensive_closure.value(intensity)
            );
        } else {
            if random_num == 3 {
                println!("Take a break today! Remember to stay hydrated!");
            } else {
                // println!(
                //     "Today, run for {} minutes!",
                //     simulated_expensive_calculation(intensity)
                // );
    
                // println!(
                //     "Today, run for {} minutes!",
                //     expensive_result
                // );
    
                println!(
                    "Today, run for {} minutes!",
                    expensive_closure.value(intensity)
                );
            }
        }
    }
    
    //custom iterator
    struct Counter {
        count: u32,
    }
    
    impl Counter {
        fn new() -> Counter {
            Counter { count: 0 }
        }
        
    }
    
    impl Iterator for Counter {
        type Item = u32; //associated type
    
        fn next(&mut self) -> Option<Self::Item> {
            self.count += 1;
            if self.count < 6{
                Some(self.count)
            } else {
                None
            }
        }
    }
    
    //Zero Cost Abstractions: the abstraction doesn’t incur any additional runtime overhead
    //because the compiler generates code as if we had written the lower-level or more repetitive code by hand.
    
    #[cfg(test)]
    mod tests {
        #[test]
        fn call_with_different_values(){
            let mut c = super::Cacher::new(|a| a);
    
            let v1 = c.value(1);
            assert_eq!(v1, 1);
            let v2 = c.value(2);
            assert_ne!(v2, 2);
        }
    
        #[test]
        fn iterator_demonstration(){
            let v1 = vec![1, 2, 3];
            let mut v1_iter = v1.iter(); //v1_iter is mutable
            assert_eq!(v1_iter.next(), Some(&1)); //next is a consuming adaptor because it takes ownership of the iterator
            assert_eq!(v1_iter.next(), Some(&2));
            assert_eq!(v1_iter.next(), Some(&3));
            assert_eq!(v1_iter.next(), None); //next is a consuming adaptor because it takes ownership of the iterator
        }
    
        #[test]
        fn iterator_sum(){
            let v1 = vec![1, 2, 3];
            let v1_iter = v1.iter();
            let total: i32 = v1_iter.sum(); //sum is a consuming adaptor because it takes ownership of the iterator
            assert_eq!(total, 6);
        }
    
        #[test]
        fn iterator_map(){
            let v1: Vec<i32> = vec![1, 2, 3];
            let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); //map is a consuming adaptor because it takes ownership of the iterator
            assert_eq!(v2, vec![2, 3, 4]);
        }
    
    
        #[test]
        fn filter_by_size(){
            let shoes = vec![
                super::Shoe { size: 10, style: String::from("sneaker") },
                super::Shoe { size: 13, style: String::from("sandal") },
                super::Shoe { size: 10, style: String::from("boot") },
            ];
            let in_my_size = super::shoes_in_my_size(shoes, 10);
    
            assert_eq!(
                in_my_size,
                vec![
                    super::Shoe { size: 10, style: String::from("sneaker") },
                    super::Shoe { size: 10, style: String::from("boot") },
                ]
            );
        }
    
        #[test]
        fn calling_next_directly(){
            let mut counter = super::Counter::new();
            assert_eq!(counter.next(), Some(1));
            assert_eq!(counter.next(), Some(2));
            assert_eq!(counter.next(), Some(3));
            assert_eq!(counter.next(), Some(4));
            assert_eq!(counter.next(), Some(5));
            assert_eq!(counter.next(), None); //next is a consuming adaptor because it takes ownership of the iterator
        }
    
        #[test]
        fn using_other_iterator_trait_methods(){
            let sum: u32 = super::Counter::new()
                .zip(super::Counter::new().skip(1))
                .map(|(a, b)| a * b)
                .filter(|x| x % 3 == 0)
                .sum(); //sum is a consuming adaptor because it takes ownership of the iterator
            assert_eq!(18, sum);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281

    Improving Our I/O Project: mini_grep

    main.rs

    //grep: globally search a regular expression and print
    
    use std::env;//command line arguments
    use std::process;//exit
    
    use minigrep::Config;//Config struct
    use minigrep::run;//run function
    
    
    //Separation of Concerns for Binary Projects
    //Splitting code into a main.rs and a lib.rs is a good default choice when starting a binary project.
    
    //1. Split your program into a main.rs and a lib.rs and move your program’s logic to lib.rs.
    //2. As long as your command line parsing logic is small, it can remain in main.rs.
    //3. When the command line parsing logic starts getting complicated, extract it from main.rs and move it to lib.rs.
    
    fn main() {
        // let args: Vec = env::args().collect();//collect command line arguments
        
        let args = env::args();//iterator over the command line arguments
        
        // println!("{:?}", args);//print command line arguments //[./target/debug/minigrep, xxxx, yyyy]
    
        // let query = &args[1];//query string 
        // let filename = &args[2];//filename
        
        //let (query, filename) = parse_config(&args[1..]);//parse command line arguments
    
        // let config = parse_config(&args);//parse command line arguments
    
        // let config = Config::new(&args);//parse command line arguments
    
        let config = Config::build(args).unwrap_or_else(|err| {
            // println!("Problem parsing arguments: {}", err);
            eprintln!("Problem parsing arguments: {}", err);//error handling: print to stderr
            process::exit(1);
        });//parse command line arguments
    
        // println!("Searching for {}", query);
        // println!("In file {}", filename);
    
        // let contents = fs::read_to_string(config.filename)
        //     .expect("Something went wrong reading the file");//read file
    
        // println!("With text:\n{}", contents);
    
        if let Err(e) = run(config){
            // println!("Application error: {}", e);
            eprintln!("Application error: {}", e);//error handling: print to stderr
            process::exit(1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    lib.rs

    use std::fs;//file system
    use std::error::Error;//error handling
    use std::env;//environment variables
    
    // fn parse_config(args: &[String]) -> (&str, &str) {
    //     let query = &args[1];//query string 
    //     let filename = &args[2];//filename
    
    //     (query, filename)
    // }
    
    pub fn run(config: Config) -> Result<(), Box<dyn Error>>{
        let contents = fs::read_to_string(config.filename)?;
        //.expect("Something went wrong reading the file");//read file
        //println!("With text:\n{}", contents);
        let results = if config.case_sensitive {//if case sensitive
            search(&config.query, &contents)//search case sensitive
        } else {
            search_case_insensitive(&config.query, &contents)//search case insensitive
        };
        // for line in search(&config.query, &contents) {//iterate over each line
        //     println!("{}", line);//print line
        // }
        for line in results {//iterate over each line
            println!("{}", line);//print line
        }
        Ok(())
    }
    
    pub struct Config {
        query: String,
        filename: String,
        case_sensitive: bool,
    }
    
    
    // fn parse_config(args: &[String]) -> Config {
    //     let query = args[1].clone();//query string 
    //     let filename = args[2].clone();//filename
    
    //     Config { query, filename }
    // }
    
    impl Config {
        // fn new(args: &[String]) -> Config {
        //     if args.len() < 3 {
        //         panic!("not enough arguments");
        //     }
        //     let query = args[1].clone();//query string 
        //     let filename = args[2].clone();//filename
    
        //     Config { query, filename }
        // }
    
        //pub fn build(args: &[String]) -> Result {
        pub fn build(mut args: std::env::Args) -> Result<Config, &'static str> {
            if args.len() < 3 {
                return Err("not enough arguments");
            }
    
            // let query = args[1].clone();
            // let filename = args[2].clone();
            args.next();//skip program name
            let query = match args.next() {//query string 
                Some(arg) => arg,
                None => return Err("Didn't get a query string"),
            };
    
            let filename = match args.next() {//filename
                Some(arg) => arg,
                None => return Err("Didn't get a file name"),
            };
    
    
            let case_sensitive = env::var("CASE_INSENSITIVE").is_err();//case sensitive
            Ok(Config { 
                query, 
                filename, 
                case_sensitive 
            })
        }
    }
    
    pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {//<'a> lifetime annotation
        // let mut results = Vec::new();//mutable vector
    
        // for line in contents.lines() {//iterate over each line
        //     if line.contains(query) {//if line contains query
        //         results.push(line);//add line to results
        //     }
        // }
        // results//return results
    
        contents.lines()//iterate over each line
            .filter(|line| line.contains(query))//if line contains query
            .collect()//collect into vector
    }
    
    pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {//<'a> lifetime annotation
        //let mut results = Vec::new();//mutable vector
        let query = query.to_lowercase();//convert query to lowercase
        // for line in contents.lines() {//iterate over each line
        //     if line.to_lowercase().contains(&query) {//if line contains query
        //         results.push(line);//add line to results
        //     }
        // }
        // results//return results
        contents.lines()//iterate over each line
            .filter(|line| line.to_lowercase().contains(&query))//if line contains query
            .collect()//collect into vector
    }
    
    //TDD: Test-Driven Development 
    //Writing a Failing Test and Seeing It Pass
    //1. Write a test that fails and run it to make sure it fails for the reason you expect.
    //2. Write or modify just enough code to make the new test pass.
    //3. Refactor the code you just added or changed and make sure the tests continue to pass.
    //4. Repeat from step 1!
    
    #[cfg(test)]
    mod tests {
        use super::*;//import outer scope
    
        #[test]
        fn one_result() {
            let query = "duct";
            let contents = "\
    Rust:
    safe, fast, productive.
    Pick three.";
            assert_eq!(
                vec!["safe, fast, productive."],
                search(query, contents)
            );
        }
    
        #[test]
        fn case_sensitive() {
            let query = "duct";
            let contents = "\
    Rust:
    safe, fast, productive.
    Pick three.
    Duct tape.";
            assert_eq!(
                vec!["safe, fast, productive."],
                search(query, contents)
            );
        }
    
        #[test]
        fn case_insensitive() {
            let query = "rUsT";
            let contents = "\
    Rust:
    safe, fast, productive.
    Pick three.
    Trust me.";
            assert_eq!(
                vec!["Rust:", "Trust me."],
                search_case_insensitive(query, contents)
            );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
  • 相关阅读:
    Qt绘图系统
    GitHub/Gitee静态页托管页部署SSL证书
    python flask框架接受axios发送的图片文件
    MongoDB学习二:基本常用命令--增删改查
    SpringCloud Stream消息驱动代码实战
    Spring Cloud的Feign和ribbon和Hystrix超时时间设置及关系
    酷柚易汛ERP - 盘点操作指南
    【教程】Python科研数据可视化、MATLAB科研数据可视化
    复习单片机:中断系统(内含1.中断概念+2 中断结构及相关寄存器)(注:相关寄存器是重点)
    Java中的SPI原理浅谈
  • 原文地址:https://blog.csdn.net/weixin_45347752/article/details/134439832