1 接受命令行参数
- 用
std::env::args()
方法获取命令行参数,得到的是std::env::Args
类型,它实现了单向(Iterator
)、双向迭代器(DoubleEndedIterator
) - 用
Iterator
的collect()
方法可以将迭代器中的元素收集到一个集合对象中,如Vector
- 将
Iterator
的collect()
方法返回值赋值给变量时,通常需要指明变量类型,因为方法返回泛型类型,通常无法自动推导数据类型
use std::env;
pub fn main() {
let args : Vec<String> = env::args().collect();
println!("{:?}",args);
let filename = &args[1];
let query = &args[2];
println!("filename: {}",filename);
println!("query: {}",query);
}
2 读取文件
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::error::Error;
pub fn main2() -> Result<String,Box<Error>>{
let args : Vec<String> = env::args().collect();
println!("{:?}",args);
let filename = &args[1];
let query = &args[2];
println!("filename: {}",filename);
println!("query: {}",query);
let mut content = String::new();
File::open(filename)?.read_to_string(&mut content)?;
Ok(content)
}
3 重构
对二进制程序进行关注分离的建议过程:
- 将程序拆分成
main.rs
和lib.rs
,将程序逻辑放入lib.rs
中 - 当命令行解析逻辑比较小时,可以保留在
main.rs
中 - 当命令行解析逻辑变得复杂时,也将其从
main.rs
中提取到lib.rs
中 - 经过上述步骤之后,
main
函数的责任应该被限制为:- 使用参数值调用命令行解析逻辑
- 设置任何其他配置
- 调用
lib.rs
中的run
函数 - 如果
run
函数返回错误,则处理这个错误
3.1 提取参数解析器
pub fn main() {
let args: Vec<String> = env::args().collect();
let (filename, query) = parse_config(&args);
………………
}
fn parse_config(args: &[String]) -> (&str, &str) {
let filename = &args[1];
let query = &args[2];
(filename, query)
}
3.2 组合配置值
struct Config {
filename: String,
query: String,
}
pub fn main() {
let args: Vec<String> = env::args().collect();
let config = parse_config(&args);
…………
}
fn parse_config(args: &[String]) -> Config {
// 这里如果不使用clone,则涉及较复杂的生命周期,对于简单的程序,将得不偿失
let filename = args[1].clone();
let query = args[2].clone();
Config {
filename: filename,
query: query,
}
}
3.3 为配置增加构造函数
struct Config {
filename: String,
query: String,
}
pub fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&args);
…………
}
impl Config {
fn new(args: &[String]) -> Config {
let filename = args[1].clone();
let query = args[2].clone();
Config {
filename: filename,
query: query,
}
}
}
3.4 改进错误处理
struct Config {
filename: String,
query: String,
}
pub fn main() {
let args: Vec<String> = env::args().collect();
// 调用 Result类型的 unwrap_or_else() 方法
let config = Config::new(&args).unwrap_or_else(|err|{
println!("解析参数失败: {}",err);
process::exit(1);
});
…………………………
}
impl Config {
// 返回值变成了 Result 类型
fn new(args: &[String]) -> Result<Config,&'static str>{
if args.len() < 3 {
return Err("参数太少")// 这里返回Err
}
let filename = args[1].clone();
let query = args[2].clone();
// 这里返回 Result 类型
Ok(Config {
filename: filename,
query: query,
})
}
}
3.5 从main
中提取逻辑
pub fn main() {
let args: Vec<String> = env::args().collect();
// 调用 Result类型的 unwrap_or_else() 方法
let config = Config::new(&args).unwrap_or_else(|err|{
println!("解析参数失败: {}",err);
process::exit(1);
});
// 注意 if let 的使用
if let Err(err) = run(config){
println!("读取文件失败: {}",err);
process::exit(2);
}
}
fn run(config : Config) -> Result<(),Box<Error>>{
let mut content = String::new();
// 注意 ? 运算符的使用
File::open(config.filename)?.read_to_string(&mut content)?;
println!("With text:\n{}", content);
Ok(())
}
4 将代码拆分到crate
// 这是 main.rs
use std::env as vne;
use std::process;
extern crate chap12;
fn main(){
section3_step7();
}
fn section3_step7() {
let args: Vec<String> = vne::args().collect();
println!("{:?}", args);
// unwrap_or_else
let config = chap12::Config::new(&args).unwrap_or_else(|err|{
eprintln!("解析参数失败: {}",err);
process::exit(1);
});
println!("文件: {} 查找: {}",config.filename,config.query);
// if let 的使用
if let Err(err) = chap12::run(config){
eprintln!("读取文件失败: {}",err);
process::exit(2);
}
}
// 这是 lib.rs
use std::fs::File;
use std::io::prelude::*;
use std::error::Error;
pub struct Config {
pub filename: String,
pub query: String,
pub case_insensitive: bool,
}
pub fn run(config : Config) -> Result<(),Box<Error>>{
let mut content = String::new();
// ? 运算符的使用
let mut file = File::open(config.filename)?.read_to_string(&mut content)?;
println!("With text:\n{}", content);
// if 表达式
let result = if config.case_insensitive{
search(&config.query,&content)
}else{
search_insensitive(&config.query,&content)
};
println!("结果: {:?}",result);
Ok(())
}
impl Config {
pub fn new(args: &[String]) -> Result<Config,&'static str>{
if args.len() < 3 {
return Err("参数太少")
}
let filename = args[1].clone();
let query = args[2].clone();
Ok(Config {
filename: filename,
query: query,
case_insensitive: std::env::var("CASE_INSENSITIVE").is_err(),
})
}
}
pub fn search<'a>(query:&str,content:&'a str) -> Vec<&'a str>{
let mut result = Vec::new();
for line in content.lines(){
if line.contains(query){
result.push(line);
}
}
result
}
pub fn search_insensitive<'a>(query:&str,content:&'a str) -> Vec<&'a str>{
let mut result = Vec::new();
let query = query.to_lowercase();
for line in content.lines(){
if line.to_lowercase().contains(&query){
result.push(line);
}
}
result
}