在本文中,我们开始用 Rust 构建一个类似 grep
的程序。我们涵盖了读取命令行参数、读取文件内容,并开始通过将程序构造为函数和结构体来重构程序。
Introduction 介绍
Running the following command will create a new project
运行以下命令将创建一个新项目
$ cargo new minigrepCreated binary (application) `minigrep` project
$ cd minigrep
The program that we are building will take in two arguments :
我们正在构建的程序将接受两个参数:
- search_string: String = This will be the key that we are searching for
search_string: String = 这将是我们正在搜索的键 - file_name: String = This is the file that is going to be searched
file_name: String = 这是要搜索的文件
The run command is going to look something like this
运行命令看起来像这样
$ cargo run search_string file_name
Reading arguments 阅读论证
To read arguments, we need to import the env
module from the std
library.
要读取参数,我们需要从 std
库导入 env
模块。
And call the env::args().collect()
method, this will return an iterator that we can use.
并调用 env::args().collect()
方法,这将返回一个我们可以使用的迭代器。
use std::env;fn main(){let args: Vec<String> = env::args().collect();println!("{:#?}",args);
}
Running this and passing some arguments will give the following output
运行它并传递一些参数将给出以下输出
$ cargo run minigrep projectFinished dev [unoptimized + debuginfo] target(s) in 0.00sRunning `target/debug/minigrep minigrep project`["target/debug/minigrep","minigrep","project",
]
As you can see we got the arguments that we passed into the project
正如你所看到的,我们得到了传递到项目中的参数
Here we don’t really care about the binary path, all we need is the last two elements of this vector.
这里我们并不真正关心二进制路径,我们只需要这个向量的最后两个元素。
Let’s get to organizing this data…
让我们开始组织这些数据......
use std::env;fn main(){let args: Vec<String> = env::args().collect();let query = &args[1];let filename = &args[2];}
Reading a file 读取文件
Now that we know what file to read, let’s get to reading?
现在我们知道要读取什么文件了,让我们开始读取吧?
To read, first let’s just create a file in the root of out crate, I am gonna name it lorem.txt
and have a bunch of lorem ipsum in there…
为了阅读,首先让我们在板条箱的根目录中创建一个文件,我将其命名为 lorem.txt
并在其中放置一堆 lorem ipsum...
$ cat lorem.txt
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
To read files we need to use the fs
module from the standard library, FS stands for file system…
要读取文件,我们需要使用标准库中的 fs
模块,FS代表文件系统......
Then we’ll use the function, read_to_string
with the file path as the function argument to get the output as a string, this function returns a Result
enum, In the error case we will print out “something went wrong reading the file”
然后我们将使用函数 read_to_string
以文件路径作为函数参数来获取字符串输出,该函数返回一个 Result
枚举,在错误情况下我们将打印出“读取文件时出错”
use std::env;
use std::fs;fn main(){let args: Vec<String> = env::args().collect();let query = &args[1];let file = &args[2];let contents = fs::read_to_string(file).expect("Something went wrong reading the file");println!("file contents: {}",contents);
}
Output: 输出:
$ cargo run example lorem.txtfile contents: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
Refactoring 重构
Now that we have the basics of the program setup, lets refactor the program…
现在我们已经有了程序设置的基础知识,让我们重构程序......
Our main function has a lot of responsibilities at the moment, So today we will distribute it into functions and structs, and do the rest in tomorrow’s article.
我们的 main 函数目前有很多职责,所以今天我们将把它分成函数和结构体,剩下的在明天的文章中完成。
Lets take a look at the code and then the line by line explanation
让我们看一下代码,然后逐行解释
use std::env;
use std::fs;
use std::process;
use std::error::Error;struct Config {query: String,file: String,
}impl Config{fn new(args: &[String]) -> Result<Config, &str>{if args.len() < 3{return Err("Not enough arguments.");}let query: String = args[1].clone();let file: String = args[2].clone();Ok(Config{query,file})}
}fn run(config: Config) -> Result<(), Box<dyn Error>>{let contents = fs::read_to_string(config.file)?;println!("file contents: {}",contents);Ok(())
}fn main(){let args: Vec<String> = env::args().collect();let config = Config::new(&args).unwrap_or_else(|err|{println!("Problem parsing arguments: {}",err);println!("Expected: {} search_query filename", args[0]);process::exit(1);});if let Err(e) = run(config) {println!("Application error: {}",e);process::exit(1);}
}
Explanation: 解释:
use std::env;
use std::fs;
use std::process;
use std::error::Error;
These lines import specific modules from the standard library (std
).
这些行从标准库 ( std
) 导入特定模块。
env
: Provides functions for interacting with the environment (e.g., command-line arguments).env
:提供与环境交互的函数(例如命令行参数)。fs
: Offers file system operations like reading and writing files.fs
:提供文件系统操作,例如读取和写入文件。process
: Provides functions for interacting with processes (e.g., exiting a process).process
:提供与进程交互的功能(例如,退出进程)。error::Error
: Imports theError
trait, which is used for error handling.error::Error
:导入Error
特征,用于错误处理。
struct Config {query: String,file: String,
}
- Defines a struct named
Config
with two fields:query
andfile
, both of typeString
.
定义一个名为Config
的结构体,其中包含两个字段:query
和file
,均为String
类型。
impl Config{fn new(args: &[String]) -> Result<Config, &str>{if args.len() < 3 {return Err("Not enough arguments.");}let query: String = args[1].clone();let file: String = args[2].clone();Ok(Config{query, file})}
}
Implements methods for the Config
struct.
实现 Config
结构的方法。
- Defines a constructor method
new
for creating a newConfig
instance.
定义一个构造函数方法new
用于创建新的Config
实例。 - Takes a slice of strings (
&[String]
) representing command-line arguments as input.
将表示命令行参数的字符串片段 (&[String]
) 作为输入。 - Returns a
Result
whereOk
contains aConfig
instance if arguments are sufficient, andErr
contains an error message otherwise.
如果参数足够,则返回Result
,其中Ok
包含Config
实例,否则Err
包含错误消息。
fn run(config: Config) -> Result<(), Box<dyn Error>>{let contents = fs::read_to_string(config.file)?;println!("file contents: {}",contents);Ok(())
}
Defines a function run
that takes a Config
instance as input.
定义一个函数 run
,它将 Config
实例作为输入。
- Attempts to read the contents of the file specified in the
Config
.
尝试读取Config
中指定的文件的内容。 - Prints the contents of the file.
打印文件的内容。 - Returns
Ok(())
if successful, indicating no error.
如果成功则返回Ok(())
,表示没有错误。
fn main(){let args: Vec<String> = env::args().collect();let config = Config::new(&args).unwrap_or_else(|err|{println!("Problem parsing arguments: {}",err);println!("Expected: {} search_query filename", args[0]);process::exit(1);});if let Err(e) = run(config) {println!("Application error: {}",e);process::exit(1);}
}
Defines the main
function, the entry point of the program.
定义 main
函数,程序的入口点。
- Retrieves command-line arguments and collects them into a vector of strings.
检索命令行参数并将它们收集到字符串向量中。 - Attempts to create a
Config
instance from the command-line arguments.
尝试从命令行参数创建Config
实例。 - If successful, continues with the program execution.
如果成功,则继续执行程序。 - If unsuccessful, prints an error message and exits the program.
如果不成功,则打印错误消息并退出程序。 - Calls the
run
function with theConfig
instance.
使用Config
实例调用run
函数。 - Handles any errors that occur during the execution of
run
by printing an error message and exiting the program.
通过打印错误消息并退出程序来处理run
执行期间发生的任何错误。
let config = Config::new(&args).unwrap_or_else(|err|{println!("Problem parsing arguments: {}",err);println!("Expected: {} search_query filename", args[0]);process::exit(1);});
- This is a closure, an error-handling mechanism used when creating a
Config
instance from command-line arguments. It prints error details and expected usage if parsing fails, then exits the program with an error code. We will study about closures in detail in the upcoming articles
这是一个闭包,是从命令行参数创建Config
实例时使用的错误处理机制。如果解析失败,它会打印错误详细信息和预期用法,然后使用错误代码退出程序。我们将在接下来的文章中详细研究闭包