Crash Rust
学习一种新的语言,首先需要了解的就是该语言的基本设计思路,工程架构特点,本文希望可以帮住大多数对Rust感兴趣的同学快速进入具体工程开发,并扫清大部分除基本语法之外的障碍; 具体涉及到的主题包括安装,运行,发布,三方包引入等等;
Rust安装
install
安装Rust非常简单,只需要一条命令,但是注意部分机器curl版本可能导致命令执行失败,比如梁小孩自己的开发机ubuntu20.04自带的curl提示ssl 443错误,如果遇到的话,尝试重新安装curl
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
check
执行Rustup命令, 注意如果是第一次全新安装, 先执行 source $HOME/.cargo/env
root@ecs-x86 :~/docs/Rust/CrashRustIn2Hours# source $HOME/.cargo/env
root@ecs-x86 :~/docs/Rust/CrashRustIn2Hours# rustup
Rustup 1.24.2 (755e2b07e 2021-05-12)
...
Hello Rust!
接下来我们完成第一个Rust项目,功能只有一句打印输出,但是大家在此章节应该学到的是如何编译并运行Rust代码程序;
编码
创建第一个rs
文件(hello.rs
),文件内容如下:
fn main() {
println!("hello Rust!");
}
文件目录结构简单,代码目录下只有一个单独的hello.rs
文件如下:
root@ecs-x86 CrashRustIn2Hours# tree code/
code/
└── hello.rs
0 directories, 1 file
编译&执行
Rust的编译器叫做rustc
,编译时直接后跟待编译的rs
文件即可, 不执行输出文件名则生成同名的可执行文件, 可以直接执行输出文件得到程序结果
root@ecs-x86 code# rustc hello.rs
root@ecs-x86 code# tree
.
├── hello
└── hello.rs
0 directories, 2 files
root@ecs-x86 code# ls -lhF
total 3.3M
-rwxr-xr-x 1 root root 3.3M Jun 2 13:32 hello*
-rw-rw-rw- 1 root root 42 Jun 2 13:29 hello.rs
root@ecs-x86 code# ./hello
hello Rust!
到此你看到的是纯手工开发流程,包括编译,文件创建,目录组织等等都需要自行维护,为了方便工程化,并且快速创建工程,Rust提供了自己的自动化工具链cargo
;
Hello Cargo!
cargo之于Rust
犹如npm之于node
, cargo
可以帮助你维护包依赖关系,安装三方包,自动编译代码,执行结果,完善debug
和release
的各种部署需求;
cargo
三板斧:
-
cargo init
自动创建工程,包括基本的配置文件,main.rs
生成等等
root@ecs-x86 code# cargo init
Created binary (application) package
root@ecs-x86 code# tree
.
├── Cargo.toml
└── src
└── main.rs
1 directory, 2 filese
-
cargo run
自动编译并执行;
注意,此时编译选项均为debug模式,所以target目录下只有一个debug目录自动生成(可执行文件亦在其中)
root@ecs-x86 code# cargo run
Compiling code v0.1.0 (/root/docs/Rust/CrashRustIn2Hours/code)
Finished dev [unoptimized + debuginfo] target(s) in 1.34s
Running `target/debug/code`
Hello, world!
root@ecs-x86 code# tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── src
│ └── main.rs
└── target
├── CACHEDIR.TAG
└── debug
-
cargo build
单独编译,如果需要单独build可能是最终发布二进制程序,此时一般附带参数--release
root@ecs-x86 code# cargo build --release
Compiling code v0.1.0 (/root/docs/Rust/CrashRustIn2Hours/code)
Finished release [optimized] target(s) in 0.35s
root@ecs-x86 code# tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── src
│ └── main.rs
└── target
├── CACHEDIR.TAG
├── debug
└── release
4 directories, 4 files
crate和module
crate
是rust对外分发和代码共享的单位,类似jar包,类似so库,是三方库的概念
-
crate
是编译打包的概念 -
crate
的核心标志就是一个单独的Cargo.toml
文件,是一个逻辑可编译的功能库 -
cargo package
可以在target/package/
目录下生成对应的*.crate
打包文件
module
是模块的概念,是代码组织方式,类似于c++的namespace
, 类似于golang的package
的概念
-
module
的核心标志是语法层面的use <module_name>
的导入和声明 module
有三种文件组织方式(假设建立一个叫做string_util
的module)
- 内嵌文件中使用
mod string_util { ... }
的方式进行定义, 内部可以包含任意多函数,结构体等等 - 建立一个独立文件名
string_util.rs
,内部无上面的显式mod <module_name>
声明 - 建立一个文件夹
string_util
内部包含一个mod.rs
文件,还有其他submodule
的话文件夹中一般使用第二种创建新文件
- 三者取其一,如果发现都没有或者有多重情况定义同一个
mod
的时候rustc便会报错.
关于crate
的其他理解可以参考官方doc的部分描述:
A crate is a compilation unit in Rust. Whenever
rustc some_file.rs
is called,some_file.rs is treated as the crate file.
If some_file.rs has mod declarations in it, then the contents of the module files would be inserted in places where mod declarations in the crate file are found, before running the compiler over it. In other words, modules do not get compiled individually,only crates get compiled.
Rust thinks in modules, not files. There is no such thing as file imports, Important concepts in the Rust Module System are
packages
,crates
,modules
, andpaths
学习module
和crate
的时候需要了解的基本前提知识如下:
rust的
pubilc
和private
的作用域控制结构体层面提升到了mod
范围,结构体本身没有类似的概念,这一点相比c++,java,golang均不同,此处的修改省去了friend 友元
,gettter
,setter
等等一些列不必要的麻烦, 同一个module
内部的所有结构体函数等等可以紧密合作,简单直接,对外暴露的函数和类型单独增加pub
关键字导出, 优雅而且编码友好
crate的感官理解
rust共享代码模块的公网地址为crates.io
, 如果使用过maven
,rpm
等类似工具应该对此类地址并不陌生;
现在我们拆解一个crates.io
上下载最多的rand
模块看一下其中的目录结构,了解一下一个rust crate大致有什么样的目录和文件结构
➜ rand git:(master) tree -L 1
.
├── CHANGELOG.md
├── COPYRIGHT
├── Cargo.toml # 核心的Cargo.toml文件
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── SECURITY.md
├── benches
├── examples
├── rand_chacha
├── rand_core # 一个单独的子crate
├── rand_distr
├── rand_hc
├── rand_pcg
├── rustfmt.toml
├── src # `rand`的src目录
└── utils
9 directories, 8 files
## lib.rs 是比较特殊的一个文件名,一个crate一般只有一个,是默认的crate的导出点和入口文件.
➜ rand git:(master) tree -L 1 src
src
├── distributions
├── lib.rs
├── prelude.rs
├── rng.rs
├── rngs
└── seq
3 directories, 3 files
## 仔细研究`rand_core`你会发现它和外部的`rand`是同构的
➜ rand git:(master) tree -L 2 rand_core
rand_core
├── CHANGELOG.md
├── COPYRIGHT
├── Cargo.toml # `rand_core`的`Cargo.toml` crate描述文件
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
└── src
├── block.rs
├── error.rs
├── impls.rs
├── le.rs
├── lib.rs # `rand_core`的`src/lib.rs`文件
└── os.rs
1 directory, 12 files
可以看到,rand crate
本身是以外部依赖
其他crate
(比如rand_core
), 我们自己写代码的时候也可以按照这种方式组织代码,虽然工程上看代码属于同一个目录结构和工程内,但是实际上其中的代码编译关系是有明确的分割关系的. 因为crate
本质上是类库的同等地位,所以一个crate
只允许一个lib
(lib.rs)编译入口文件,但是可以允许有多个main
函数(依赖核心的lib.rs)单独编译可执行文件(binary crate
);
注释和文档
相比于C++等古老的语言生态,rust的生态支持解决了太多痛点,比如crates.io
的存在保证了代码的编写,发布,文档的一体化,一切都是简单的cargo命令即可完成;既然说到了文档,就要知道cargo还有cargo doc
命令,只要使用markdown编写的注释便可直接生成对应的html文档
,其开发友好程度让人大呼过瘾.
rust的注释写法简单说有如下几种:
-
// 普通注释
,此类注释cargo doc
会忽略 -
/// 文档注释
,一般函数结构提元素的注释写法,内容可以是markdown语法,如果包含代码段,代码会被自动生成测试代码 -
//! 高层的文档注释
,此类注释生成的是高层模块描述和简介
cargo new --lib myutil
# 修改src/lib.rs为如下内容
//! # The first line
//! The second line
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, add_one(5));
/// # fn add_one(x: i32) -> i32 {
/// # x + 1
/// # }
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
以上面代码为例,生成文档的过程如下:
cargo doc --no-deps
cargo doc --open