rust成长之路-minigrep

本文介绍了如何在Rust程序中处理命令行参数,使用`std::env::args`获取参数,定义`Config`结构体存储参数,实现构造函数并处理错误。还展示了如何读取文件、封装函数以及模块化的代码组织。
摘要由CSDN通过智能技术生成

一.接受运行参数

为了可以接受运行是的参数,需要使用std::env::args

// 可通过collect函数将读取的参数转化为Vec
// cargo run -- test file.txt
// -- 后面的传递给程序的参数
// 第一个参数是默认是工程名字
// test file.txt 才是我们需要的参数
let args: Vec<String> = env::args().collect();

println!("args{:?}", args);

1.2 存储参数

目前已经可以接受参数,参数存放到了args中,下一步我们定义一个结构体保存参数,当前程序需要两个参数,第一个是搜索的字符串,第二个是文件名字,我们可以使用两种方式定义:

// 直接使用自字符串
struct Config {
    grep: String,
    file_name: String,
}

// 使用引用,注意要标注生存期
struct Config<'a> {
    grep: &'a str,
    file_name: &'a str,
}

1.3 构造函数定义

有了数据结构,我们可以使用构造函数接受数据参数构造:

// 使用引用时
impl<'a> Config<'a> {
    // 静态生存期
    fn new(command: &[String]) -> Result<Config, &'static str> {
        if command.len() < 3 {
            return Err("not enough arguments");
        }
        Ok(Config {
            grep: &command[1],
            file_name: &command[2],
        })
    }
}



// 入参是引用借用关系,无法转移变量,因此需要copy
impl Config {
    // 静态生存期
    fn new(command: &[String]) -> Result<Config, &'static str> {
        if command.len() < 3 {
            return Err("not enough arguments");
        }
        Ok(Config {
            grep: command[1].clone(),
            file_name: command[2].clone(),
        })
    }
}

返回Result是为了方便调用方处理异常,错误信息为静态字符串,因此可以使用静态的生存期。

use std::env;
use std::process;
use std::error::Error;

fn main{
    // cargo run -- grep file.txt
    let args: Vec<String> = env::args().collect();
    
    // Result 的unwrap_or_else方法可以处理异常信息
    let config = Config::new(&args).unwrap_or_else(|e| {
        print!("error: {e}");
        // 退出程序
        process::exit(1);
    });

    println!("query: {}, path: {}!", config.grep, config.file_name);


}

二.读取文件

2.1 新建文件

新建一个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!

2.2 读取文件

读取文件需要使用std::fs::read_to_string

#[test]
fn test_run() {
    let contents = fs::read_to_string("file.txt").expect("need a file");
    print!("contents:\n {:}", contents);
}

2.3 封装文件读取函数

// dyn dynamic缩写,代表错误类型是动态的
// ? 操作符是语法糖,返回值为Result<T,E>的函数,rust会自动转化,发生错误时自动结束返回
fn run(config: Config) -> Result<(), Box<dyn Error>> {
    // 语法糖
    let contents = fs::read_to_string(config.file_name)?;
    print!("contents:\n {}", contents);
    Ok(())
}

main函数:

use std::env;
use std::process;
use std::error::Error;
use std::fs;

fn main() {
    // 获得运行参数
    // cargo run -- grep file.txt
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|e| {
        print!("error: {e}");
        process::exit(1);
    });

    println!("query: {}, path: {}!", config.grep, config.file_name);
    if let Err(e) = run(config) {
        print!("error: {e}");
        process::exit(1);
    }
}

 struct Config {
    grep: String,
    file_name: String,
}


// 引用需要标记生存期
impl Config {
    // 静态生存期
    fn new(command: &[String]) -> Result<Config, &'static str> {
        if command.len() < 3 {
            return Err("not enough arguments");
        }
        Ok(Config {
            grep: command[1].clone(),
            file_name: command[2].clone(),
        })
    }
}

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    // 语法糖
    let contents = fs::read_to_string(config.file_name)?;
    print!("contents:\n {}", contents);
    Ok(())
}



三. 代码整合

上述完成了对参数的存储,文件的读取,但是有写代码经验的同学,可能会将这些方法封装成模块暴露出来使用,对程序稍作修改即可:

新建lib.rs到src目录:

use std::error::Error;
use std::fs;

pub struct Config<'a> {
    pub grep: &'a str,
    pub file_name: &'a str,
}


// 引用需要标记生存期
impl<'a> Config<'a> {
    // 静态生存期
    pub fn new(command: &[String]) -> Result<Config, &'static str> {
        if command.len() < 3 {
            return Err("not enough arguments");
        }
        Ok(Config {
            grep: &command[1],
            file_name: &command[2],
        })
    }
}

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    // 语法糖
    let contents = fs::read_to_string(config.file_name)?;
    print!("contents:\n {}", contents);
    Ok(())
}

main函数稍作修改:
 

use std::env;
use std::process;

fn main() {
    // 获得运行参数
    // cargo run -- grep file.txt
    let args: Vec<String> = env::args().collect();

    // 这样在main函数可以使用crate::引用
    let config = minigrep::Config::new(&args).unwrap_or_else(|e| {
        print!("error: {e}");
        process::exit(1);
    });

    println!("query: {}, path: {}!", config.grep, config.file_name);
    if let Err(e) = minigrep::run(config) {
        print!("error: {e}");
        process::exit(1);
    }
}

四.搜索字符串

定义一个query方法:

// 需要指定生命周期,因为返回值编译器无法推断和哪个入参生命周期一致
fn search<'a>(query: &'a str, contents: &'a str) -> Vec<&'a str> {
    let mut values = Vec::new();
    // 使用contains方法检查query是否被包含
    for line in contents.lines() {
        if line.contains(query) {
            values.push(line);
        }
    }
    values
}

lib.rs修改入参为引用:

use std::error::Error;
use std::fs;

pub struct Config<'a> {
    pub grep: &'a str,
    pub file_name: &'a str,
}


// 引用需要标记生存期
impl<'a> Config<'a> {
    // 静态生存期
    pub fn new(command: &[String]) -> Result<Config, &'static str> {
        if command.len() < 3 {
            return Err("not enough arguments");
        }
        Ok(Config {
            grep: &command[1],
            file_name: &command[2],
        })
    }
}

pub fn run(config: &Config) -> Result<String, Box<dyn Error>> {
    // 语法糖
    let contents = fs::read_to_string(config.file_name)?;
    Ok(contents)
}

main函数调用:
 

use std::env;
use std::process;

fn main() {
    // 获得运行参数
    // cargo run -- grep file.txt
    let args: Vec<String> = env::args().collect();

    let config = minigrep::Config::new(&args).unwrap_or_else(|e| {
        print!("error: {e}");
        process::exit(1);
    });

    println!("query: {}, path: {}!", config.grep, config.file_name);
    let contents = minigrep::run(&config);
    if let Err(e) = contents {
        print!("error: {e}");
        process::exit(1);
    }
    let contents = contents.unwrap();
    let vec = search(&config.grep, &contents);
    for line in vec {
        println!("{line}");
    }
}

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




  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值