接受命令行参数
use std::env; // collect
fn main() {
let args: Vec<String> = env::args().collect();
// env::args_os() // OsString
println!("{:?}", args);
let quiery = &args[1];
let filename = &args[2];
println!("Search for {}", quiery);
println!("In file {}", filename);
}
读取文件
poem.txt
I'm nobody! Who are you?
Are you nobody, too.
Then there's a pair of us - don't tell!
They'd banish us, you know.
How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!
use std::env; // collect
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
// env::args_os() // OsString
println!("{:?}", args);
let quiery = &args[1];
let filename = &args[2];
println!("Search for {}", quiery);
println!("In file {}", filename);
// 读取文件
let contents = fs::read_to_string(filename)
.expect("Something went wrong reading the file.");
// 打印内容
println!("With text:\n{}", contents);
}
重构:改进模块和错误处理
二进制程序关注点分离的指导性原则
将程序拆分为main.rs和lib.rs,将业务逻辑放入lib.rs
当命令行解析逻辑较少时,将它放在main.rs也行
当命令行解析逻辑变复杂时,需要将它从main.rs提取到lib.rs
经过上述拆分,留在main的功能有:
使用参数值调用命令行解析逻辑
进行其他配置
调用lib.rs中的run函数
处理run函数可能出现的错误
use std::env; // collect
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
// env::args_os() // OsString
println!("{:?}", args);
let config = Config::new(&args);
// 读取文件
let contents = fs::read_to_string(config.filename)
.expect("Something went wrong reading the file.");
// 打印内容
println!("With text:\n{}", contents);
}
struct Config {
query: String,
filename: String,
}
impl Config {
fn new(args: &[String]) -> Config {
let query = args[1].clone();
let filename = args[2].clone();
Config { query, filename }
}
}
错误处理
use std::env; // collect
use std::fs;
use std::process;
fn main() {
let args: Vec<String> = env::args().collect();
// env::args_os() // OsString
println!("{:?}", args);
let config = Config::new(&args).unwrap_or_else( |err| {
println!("Problem parsing arguments: {}", err);
process::exit(1);
});
// 读取文件
let contents = fs::read_to_string(config.filename)
.expect("Something went wrong reading the file.");
// 打印内容
println!("With text:\n{}", contents);
}
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 })
}
}
将配置挪到lib.rs,注意添加pub
lib.rs(与main.rs 同级目录下)
use std::error::Error;
// collect
use std::fs;
// 读取文件
pub fn run(config: Config) -> Result<(), Box<dyn Error>>{
// 读取文件
let contents = fs::read_to_string(config.filename)?;
// 打印内容
println!("With text:\n{}", contents);
Ok(())
}
pub struct Config {
pub query: String,
pub filename: String,
}
impl Config {
pub 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(测试驱动开发)开发库功能
TDD(Test-Driven Development)
编写一个会失败的测试,运行该测试,确保它是按照预期的原因失败
编写或修改刚好足够的代码,让测试通过
重构刚刚添加或修改的代码,确保测试会始终通过
返回步骤1,继续
继续在lib.rs添加测试
use std::error::Error;
// collect
use std::fs;
// 读取文件
pub fn run(config: Config) -> Result<(), Box<dyn Error>>{
// 读取文件
let contents = fs::read_to_string(config.filename)?;
for line in search(&config.query, &contents) {
println!("{}", line);
}
// 打印内容
println!("With text:\n{}", contents);
Ok(())
}
pub struct Config {
pub query: String,
pub filename: String,
}
impl Config {
pub 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 })
}
}
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:\n \
safe, fast, productive.\n \
Pick three.";
assert_eq!(vec![" safe, fast, productive."],
search(query, contents))
}
}
cargo test
![](https://img-blog.csdnimg.cn/img_convert/ea19d7bb430b5270d46c3b5a5af6bf85.png)
cargo run body poem.txt
![](https://img-blog.csdnimg.cn/img_convert/0e631042807701614eaef24646a584ce.png)
使用环境变量
设置环境变量
impl Config {
pub 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();
let case_sensitive = env::var("CASE_INSENSITIVE").is_ok();
Ok(Config { query, filename, case_sensitive })
}
}
在命令行(Windows)输入:
set CASE_INSENSITIVE=1 | cargo run to poem.txt
得到结果:
![](https://img-blog.csdnimg.cn/img_convert/b123c5686362ec672f713fcacd861759.png)
将错误消息写入标准错误而不是标准输出
标准输出vs标准错误
标准输出:stdout
println!
不带参数输出至output.txt
cargo run > output.txt
标准错误:stderr
eprintln!
带参数输出至output.txt
cargo run to poem.txt > output.txt
![](https://img-blog.csdnimg.cn/img_convert/cee04cf658680b3cb28e86a495945d1c.png)
总结
好像回想起自己为什么学这个了——为了把自己错误的代码习惯在毕业前彻头彻尾的改一下,形成自己的代码思维!