在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
这是一份不错的rust教程,目前包括4个block和4个project。全部完成后可以用rust实现一个简单的key-value存储引擎。 注意:Windows下rust貌似会遇到一些bug,强烈建议使用Linux来开发
Building Block1一开始就是Hello World啦......通过实现一个简单的命令行程序来体验一下rust 比如我们希望程序能获得命令行参数 use std::env; fn main() { let args: Vec<String> = env::args().collect(); println!("{:?}", args); } 这一段看起来和c++差不多......(其实感觉rust比go好理解多了...)
但是一个复杂的cli程序(比如Linux中的ls),命令行参数是很复杂的。比如我们想给写个help(比如ls -h)供用户参考,该怎么办呢?我们可以使用rust的clap库来实现。 首先需要定义一个yml,里面定义命令行参数的格式,保存为/src/cli.yml name: myapp version: "1.0" author: Kevin K. <kbknapp@gmail.com> about: Does awesome things args: - config: short: c long: config value_name: configval help: Sets a custom config file takes_value: true - INPUT: help: Sets the input file to use required: true index: 1 - verbose: short: v multiple: true help: Sets the level of verbosity subcommands: - test: about: controls testing features version: "1.3" author: Someone E. <someone_else@other.com> args: - debug: short: d help: print debug information 然后编写rust程序,保存为/src/main.rs: 1 #[macro_use] 2 extern crate clap; 3 use clap::App; 4 5 fn main() { 6 println!("Hello, world"); 7 let yaml = load_yaml!("cli.yml"); 8 let m = App::from_yaml(yaml).get_matches(); 9 10 if let Some(configval) = m.value_of("config"){ 11 match configval{ 12 "c1" => println!("config 1111"), 13 "c2" => println!("config 2222"), 14 "c3" => println!("config 3333"), 15 _ => println!("what did you config?") 16 } 17 } else { 18 println!("--config is not assigned"); 19 } 20 21 if let Some(inputval) = m.value_of("INPUT"){ 22 println!("{:?}", inputval); 23 } else { 24 println!("INPUT is not assigned"); 25 } 26 }
但是如果直接用rustc来运行上面的程序会报错噢: F:\My Drive\19fall\talent-plan\rust\building-blocks\bb1\src>rustc main.rs error[E0463]: can't find crate for `clap` --> clapusage.rs:2:1 | 2 | extern crate clap; | ^^^^^^^^^^^^^^^^^^ can't find crate error: aborting due to previous error For more information about this error, try `rustc --explain E0463`. 这是因为本地默认还没有安装clap这个库,需要手动告诉rust来安装这个库(类似pip install一下)。这一点和C++不一样哦。 为了方便起见我们改用cargo来编译运行rust。cargo是rust的构建系统和包管理器,可以帮我们自动完成下载安装依赖库的工作。为了使用cargo,我们需要一开始就用 cargo new newproj 来新建项目。新建好的项目文件夹中会有cargo.toml文件,我们打开该文件,加入以下语句来声明使用了clap中的yaml库: [dependencies.clap] features = ["yaml"] 然后使用cargo build来编译项目,使用cargo run来编译+运行,使用cargo clean来清除上次编译的结果(有点像Makefile的作用)。这里我们cargo build,然后进入/target/debug/文件夹,就可以看到编译好的可执行文件啦。 运行结果如下,可以看到既可以打印help,也可以处理命令行输入: tidb@pcserver:/mnt/toshiba/talent-plan/rust/building-blocks/bb1$ ./target/debug/bb1 -c c1 fff Hello, world config 1111 "fff" tidb@pcserver:/mnt/toshiba/talent-plan/rust/building-blocks/bb1$ ./target/debug/bb1 --help Hello, world myapp 1.0 Kevin K. <[email protected]> Does awesome things USAGE: bb1 [FLAGS] [OPTIONS] <INPUT> [SUBCOMMAND] FLAGS: -h, --help Prints help information -V, --version Prints version information -v Sets the level of verbosity OPTIONS: -c, --config <configval> Sets a custom config file ARGS: <INPUT> Sets the input file to use SUBCOMMANDS: help Prints this message or the help of the given subcommand(s) test controls testing features
处理好了命令行,可能某一天PM想让程序猿再加个读取环境变量的功能。还好系统还是提供了库函数(所以还是调包大法好?) 1 fn main() { 2 println!("Hello, world!"); 3 use std::env; 4 5 let key = "HOME"; 6 match env::var_os(key) { 7 Some(val) => println!("{}: {:?}", key, val), 8 None => println!("{} is not defined in the environment.", key) 9 } 10 } 11 12 运行结果: 13 tidb@pcserver:/mnt/toshiba/talent-plan/rust/building-blocks/bb1env$ cargo run 14 Finished dev [unoptimized + debuginfo] target(s) in 0.00s 15 Running `target/debug/bb1env` 16 Hello, world! 17 HOME: "/home/tidb"
错误处理 用户有的时候是很皮的(程序猿也是),所以程序不可避免会遇到一些异常情况。在java和c++里我们可以用 try...catch... / throw 来处理异常,rust也提供了类似的机制。比如上次改TiKV config的时候就用到了。原教程给的例子不大好...这里我们自己写一个: 1 use std::env; 2 3 enum ErrTypes{ 4 Err111, 5 // Err222, 6 } 7 8 fn getargs(args: Vec<String>) -> Result<String, ErrTypes>{ 9 match args.get(1) { 10 Some(_v) => Ok(_v.to_string()), 11 None => Err(ErrTypes::Err111) 12 } 13 } 14 15 fn main() { 16 println!("Hello, world!"); 17 let args: Vec<String> = env::args().collect(); 18 19 let val=getargs(args); 20 match val{ 21 Ok(_v) => println!("OK, val == {:?}", _v), 22 Err(_e) => println!("Error!!!!!") 23 } 24 } 这段代码的含义还是很易懂的(虽然写的时候可是debug了半天qwq),就是检测第二个命令行参数是否存在(第一个默认是调用该程序的cmd,即类似于"C:\command.com"这种)。 这里我们用Result进行了异常处理。Result也是一种枚举类型,定义如下: enum Result<T, E> {
Ok(T),
Err(E),
}
这里T和E都是泛型类型参数。比如在上面的代码中,getargs函数的返回值是Result<String, ErrTypes>类型,表示函数执行成功的时候应该返回一个String,而失败的时候返回ErrTypes(我们自己定义的一个错误类型)。[Ref] 我们运行一下看看: tidb@pcserver:/mnt/toshiba/talent-plan/rust/building-blocks/bb1err$ ./target/debug/bb1err www Hello, world! OK, val == "www" tidb@pcserver:/mnt/toshiba/talent-plan/rust/building-blocks/bb1err$ ./target/debug/bb1err Hello, world! Error!!!!!
另外block1里还有几个文档,虽然暂时用不着但可以以后留着参考:
Project1第一个project是一个简单的key-value store ,其实就是调用HashMap+处理一下命令行输入输出。那我们就开始叭 Part1 rust中的HashMap打开空白的kv.rs,可以看到里面已经有了一个半成品,我们直接往里面填空就可以啦。这个文件里定义了一个KvStore结构体,pub struct{}里面可以定义结构体成员变量(这里没有成员变量),impl KvStore{}里面可以定义结构体成员方法。 单纯操作hashmap还是很容易的...但是在这里面我们可以学习一个rust函数的操作 在这个文件里可以看到很多函数都会有一些奇怪的参数,有的是&self,有的是&mut self。另外像get和new函数还要有返回值。
Part2 处理命令行输入这部分是在kvs.rs中进行的。首先我们要use相关的库:clap(用于解析命令行参数)和exit(用于退出时命令行返回值) 根据题目要求,这个程序需要实现以下参数:
为了让代码更加整洁,我们像上面的例子一样,把命令的定义写在yml里,然后load_yaml!()来读取这些命令。set、get、rm作为subcommand,而Version作为args。 鉴于纯内存的hashmap反正退出程序之后东西都是会丢失的...就不implement命令行啦
Part3 组装起来吧!现在我们的文件结构长这样: ✉ project-1 |--✉ src | |--✉ bin | | |-- cli.yml | | |-- kvs.rs | | | |--lib.rs | |--kv.rs | |--✉ tests | |-- tests.rs | |-- Cargo.toml |-- project.md 前面写好了kv.rs来定义KvStore结构体,kvs.rs定义了main函数来处理命令行输入 lib.rs很短...就两行,用于把KvStore包含进来,相当于c++中.h的作用。(详细可参考《rust程序设计语言》的“模块系统”一节) pub use kv::KvStore;
mod kv;
全部组装好之后就可以啦!可以cargo test来测试一下结果 test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论