Rust学习笔记

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

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

Lecture 12: An I/O Project: Building a Command Line Program

project: minigrep

src/main.rs

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
//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<String> = env::args().collect();//collect 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);
}
}

lib.rs

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
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<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}

let query = args[1].clone();
let filename = args[2].clone();
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
}

//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)
);
}
}