rust例子grep、闭包、迭代器


简单的grep demo

接收命令行参数

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();


    let query = &args[1];
    let filename = &args[2];

    println!("search for: {}",query);
    println!("in file: {}",filename);
}

// > cargo run 123 456
//     Finished dev [unoptimized + debuginfo] target(s) in 0.01s
//     Running `target\debug\mingrep.exe 123 456`
// query: 123
// filename: 456

读取文件

let contents = fs::read_to_string(filename)
.expect("Something went wrong reading the file.");
println!("content: {}",contents);

重构

image-20221129162950882
// 改善模块化和错误处理

use std::{env, fs, process};

fn main() {
    let args: Vec<String> = env::args().collect();


    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}",err);
        process::exit(1);
    });

    println!("search for: {}",config.query);
    println!("in file: {}",config.filename);

    let contents = fs::read_to_string(config.filename)
        .expect("Something went wrong reading the file.");
    println!("content: {}",contents);
}

// lib.rs
struct Config {
    query: String,
    filename: String,
}

impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments.")
        }

        let query = args[1].clone();
        let filename = args[2].clone();

        Ok(Config {
            query,
            filename,
        })
    }
}

TDD测试驱动开发

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let mut results = Vec::new();
    for line in contents.lines() {
        if line.contains(query) {
            results.push(line);
        }
    }

    results
}

# [cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn one_result() {
        let query = "duct";
        let contents = "\
Rust:
Safe, fase, productive.
Pick three.";

        assert_eq!(vec!["Safe, fase, productive."], search(query,contents));
    }
}

然后将可运行的search函数代码加入原有代码中。

pub fn run(config: Config) -> Result<(), Box<dyn Error>>{
    println!("search for: {} in file: {}\n\n", config.query,config.filename);

    let contents = fs::read_to_string(config.filename)?;
    
    for line in search(&config.query, &contents) {
        println!("{}", line);
    }

    Ok(())
}

使用环境变量

let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

将错误信息输出到标准错误

// 标准输出:stdout
 println!
// 标准错误:stderr
 eprintln!

闭包

closure

可以捕获其所在环境的匿名函数。

类型推断

闭包不要求标注参数和返回值的类型。通常短小,只在狭小的上下文中工作,编译器通常能推断出其类型,也可以手动标注。

闭包的定义最终只会为参数和范沪指推断出唯一具体的类型

image-20221129191728574

存储闭包

  • 创建一个struct,持有闭包及其调用结果。

每个闭包实例有自己的唯一匿名类型。


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
            }
        }
    }
}

fn work(intensity: u32, number: u32) {
    let mut exp_closure = Cacher::new(|num| {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    });

    if intensity < 25 {
        println!("haha {}",exp_closure.value(intensity));
        println!("hengheng {}",exp_closure.value(intensity));
    } else {

    }
}

闭包捕获上下文

let x = 4;
let eqx = |z|  z==x;

let y = 4;
assert!(eqx(y));
image-20221129193949449

在参数列表前使用move关键字,可以强制闭包取得环境值的所有权。

迭代器

迭代器模式:对一系列项执行某些任务。

image-20221129194547088

#[test]
fn iterator_sum() {
    let v1 = vec![1,2,3];
    let v1_iter = v1.iter();

    let total:i32 = v1_iter.sum();
    assert_eq!(total,6);
}

map操作:

#[test]
fn iterator_sum() {
    let v1 = vec![1,2,3];
    let v2: Vec<_> = v1.iter().map(|x| x+1).collect();

    assert_eq!(v2,vec![2,3,4]);
}

闭包捕获环境:

let tt = 2;
let v3: Vec<_> = v1.into_iter().filter(|x| x%tt==1).collect();

自定义迭代器:

  • 实现next方法

将输入以迭代器方式传递:

pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
    if args.len() < 3 {
        return Err("not enough arguments.")
    }

    args.next();
    let query =  match args.next() {
        Some(arg) => arg,
        None => return Err("Didn't get a query string"),
    };
    let filename = match args.next() {
        Some(arg) => arg,
        None => return Err("Didn't get a filename")
    };
    let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

    Ok(Config {
        query,
        filename,
        case_sensitive,
    })
}