这是一份不错的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);
} 运行结果:
F:\My Drive\19fall\talent-plan\rust\building-blocks\bb1\src>main.exe 11 22
["main.exe", "11", "22"]

这一段看起来和c++差不多......(其实感觉rust比go好理解多了...)

  • println!结尾的叹号!表示调用了一个Rust宏。如果是调用函数,应该输入println

但是一个复杂的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:

 #[macro_use]
extern crate clap;
use clap::App; fn main() {
println!("Hello, world");
let yaml = load_yaml!("cli.yml");
let m = App::from_yaml(yaml).get_matches(); if let Some(configval) = m.value_of("config"){
match configval{
"c1" => println!("config 1111"),
"c2" => println!("config 2222"),
"c3" => println!("config 3333"),
_ => println!("what did you config?")
}
} else {
println!("--config is not assigned");
} if let Some(inputval) = m.value_of("INPUT"){
println!("{:?}", inputval);
} else {
println!("INPUT is not assigned");
}
}
  • 这里crate是一个二进制或库项目
  • match相当于C语言中的switch语句
  • if let xx=yy {} else {} 是一个常用的可以处理异常(比如用户没有提供这个参数)的写法

但是如果直接用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
"fff" tidb@pcserver:/mnt/toshiba/talent-plan/rust/building-blocks/bb1$ ./target/debug/bb1 --help
Hello, world
myapp 1.0
Kevin K. <kbknapp@gmail.com>
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想让程序猿再加个读取环境变量的功能。还好系统还是提供了库函数(所以还是调包大法好?)

 fn main() {
println!("Hello, world!");
use std::env; let key = "HOME";
match env::var_os(key) {
Some(val) => println!("{}: {:?}", key, val),
None => println!("{} is not defined in the environment.", key)
}
} 运行结果:
tidb@pcserver:/mnt/toshiba/talent-plan/rust/building-blocks/bb1env$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in .00s
Running `target/debug/bb1env`
Hello, world!
HOME: "/home/tidb"
  • 注意6-9行里,Some(T)和None来自于一种枚举类型Option<T>。对于env::var_os(key)的返回值,Some(val)表示结果是某个存在的值,并把它存到val变量中;而None表示返回结果是空的(相当于c语言中的NULL)。这样做的好处是,比如我们在函数返回的时候得到一个Option<i8>类型(可能是Some(i8),也可能是None),在把它转换回i8类型时就已经解决了值为空的情况(比如用6-9行的match),之后的i8类型就一定不为空了。这样就避免了c语言里空指针可能带来的问题。

错误处理

用户有的时候是很皮的(程序猿也是),所以程序不可避免会遇到一些异常情况。在java和c++里我们可以用 try...catch... / throw 来处理异常,rust也提供了类似的机制。比如上次改TiKV config的时候就用到了。原教程给的例子不大好...这里我们自己写一个:

 use std::env;

 enum ErrTypes{
Err111,
// Err222,
} fn getargs(args: Vec<String>) -> Result<String, ErrTypes>{
match args.get(1) {
Some(_v) => Ok(_v.to_string()),
None => Err(ErrTypes::Err111)
}
} fn main() {
println!("Hello, world!");
let args: Vec<String> = env::args().collect(); let val=getargs(args);
match val{
Ok(_v) => println!("OK, val == {:?}", _v),
Err(_e) => println!("Error!!!!!")
}
}

这段代码的含义还是很易懂的(虽然写的时候可是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函数还要有返回值。

  • &表示引用,它允许你使用值但不获取其所有权,意义类似于c++中的传参数指针。但rust中,函数引用来的变量在该函数中是不可被修改的。
  • mut表示该变量是可更改的。可以用& mut varname创建一个可变引用。但对于同一个变量,同一时间只能有一个可变引用,或者多个普通的不可变引用。
  • 在《rust程序设计语言》的“认识所有权”一节中,详细说明了所有权和引用的概念。
  • ::是运算符,表示指定namespace下的特定函数,也和c++一样
  • kv.rs中可以理解为定义了KvStore这个结构体的成员函数和方法。
  • impl中定义了一个new()函数,作用是初始化并返回一个KvStore结构体。它有点像c++中的构造函数,但rust中必须自行定义,因为rust中其实没有类的概念。
  • impl中定义了KvStore结构体的三个方法set、get、remove。方法的第一个参数总是self,它代表调用该方法的结构体实例(这里就是一个KvStore了,因为这三个方法都是作用在KvStore类型的结构体上的)。&self表示不可变的引用,而&mut self表示可变的引用。   同样因为rust中没有类的概念,所以需要搞一个self来接收调用该方法的结构体实例。
  • 函数如果需要返回一个值,直接写这个值即可,不需要return关键字。返回的这个值末尾也不加分号
  • .cloned()我也不知道啥意思...就先这么着吧 QAQ
  • set中的key和value不需要引用,是函数就这样要求的,不然编译会报错...

Part2 处理命令行输入

这部分是在kvs.rs中进行的。首先我们要use相关的库:clap(用于解析命令行参数)和exit(用于退出时命令行返回值)

根据题目要求,这个程序需要实现以下参数:

  • kvs set <KEY> <VALUE>        Set the value of a string key to a string
  • kvs get <KEY>                       Get the string value of a given string key
  • kvs rm <KEY>                        Remove a given key
  • kvs -V                                    Print the version

为了让代码更加整洁,我们像上面的例子一样,把命令的定义写在yml里,然后load_yaml!()来读取这些命令。set、get、rm作为subcommand,而Version作为args。

鉴于纯内存的hashmap反正退出程序之后东西都是会丢失的...就不implement命令行啦

Part3 组装起来吧!

现在我们的文件结构长这样:

✉ project-
|--✉ 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.  passed;  failed;  ignored;  measured;  filtered out

代码

Rust学习笔记1的更多相关文章

  1. Rust学习笔记一 数据类型

    写在前面 我也不是什么特别厉害的大牛,学历也很低,只是对一些新语言比较感兴趣,接触过的语言不算多也不算少,大部分也都浅尝辄止,所以理解上可能会有一些偏差. 自学了Java.Kotlin.Python. ...

  2. Rust学习笔记2

    继续继续... 转眼都开学啦... Building Blocks 2 building blocks里讲了一些关于Log structure storage的东西,这也是用于在硬盘上持久化KvSto ...

  3. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  4. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  5. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  6. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  7. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  8. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  9. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

随机推荐

  1. MySQL的视图和索引

    MySQL的视图 简单来说MySQL的视图就是对SELECT 命令的定义的一个快捷键,我们查询时会用到非常复杂的SELECT语句,而这个语句我们以后还会经常用到,我们可以经这个语句生产视图.视图是一个 ...

  2. Elastic-Job介绍

    1 什么是分布式任务调度 什么是分布式?当前软件的架构正在逐步转变为分布式架构,将单体结构分为若干服务,服务之间通过网络交互来完成用户的业务处理,如下图,电商系统为分布式架构,由订单服务.商品服务.用 ...

  3. sh_02_快速体验

    sh_02_快速体验 import sh_01_九九乘法表 sh_01_九九乘法表.multiple_table()

  4. Redis高级命令及特性(安全性)

    高级命令 ​ keys * : 返回满足的所有键 ,可以模糊匹配 ​ exists :是否存在指定的key,存在返回1,不存在返回0 ​ exprie:设置某个key的过期时间 ​ ttl:查看剩余时 ...

  5. 通过jedis连接redis单机成功,使用redis客户端可以连接集群,但使用JedisCluster连接redis集群一直报Could not get a resource from the pool

    一,问题描述: (如题目)通过jedis连接redis单机成功,使用JedisCluster连接redis集群一直报Could not get a resource from the pool 但是使 ...

  6. linux 上使用libxls读和使用xlslib写excel的方法简介

      读取excel文件:libxls-1.4.0.zip下载地址:http://sourceforge.net/projects/libxls/安装方法: ./configure make make ...

  7. HyperV - glossary

    Root Partition - sometimes called partition. Manages machine-level functions such as device drivers, ...

  8. webpack前置知识2(JavaScript项目初始化)

    所有的JavaScript项目都是在终端输入npm init -y进行项目初始化,如果要自定义项目规则,去掉 -y 参数. vscode终端快捷键ctrl+` 初始化 运行上述命令后,项目内会新建一个 ...

  9. Linux监控命令之==>free

    一.命令说明 free 命令显示系统内存的使用情况:包括物理内存.交换内存(swap)和内核缓冲区内存 二.参数说明 -b -k -m -g:分别以字节.KB.MB.GB为单位显示内存使用情况 -l: ...

  10. Python学习之==>发送邮件

    自动化测试执行完成后,需要自动发送测试报告.Python发送邮件可以使用smtplib标准模块,但该模块比较繁琐,推荐使用yagmail这个第三方模块,用法比较简单. 一.邮箱设置 1.在邮箱设置里打 ...