介绍
Rust是一种快速、高并发、安全且具有授权性,最初由 Graydon Hoare 于2006年创造和发布。现在它是一种开源语言,主要由 Mozilla 团队和许多开源社区成员共同维护和开发。它的目标是 C 和 C++ 占主导地位的系统编程领域。
优势
Rust 是一门编译语言,因此它的效率可以媲美 C 或 C++ 语言
由于没有 GC(垃圾回收机制),所以是安全级高的语言
Rust 可以做什么?
- 可以使用 Rust 编写操作系统、游戏引擎和许多性能关键型应用程序
- 可以使用它构建高性能的Web应用程序、网络服务,类型安全的数据库对象关系映射(Object Relational Mapp ORM)库,还可以将程序编译成 Web Assembly 在 Web 浏览器上运行
- Rust 还在为嵌入式平台构建安全性优先的实时应用程序方面获得了相当大的关注。例如 Arm 基于 Cortex-M 的微控制器,目前该领域主要由 C 语言主导。Rust 因其广泛的适用性在多个领域都表现良好
安装Rust
Windows
下载并运行 rustup-init.exe
Linux or Mac:
curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh
另外,你还需要一个 链接器(linker),这是 Rust 用来将其编译的输出连接到一个文件中的程 序。很可能你已经有一个了。如果你遇到了链接器错误,请尝试安装一个 C 编译器,它通常包括 一个链接器。C 编译器也很有用,因为一些常见的 Rust 包依赖于 C 代码,因此需要安装一个 C 编 译器。
在 macOS 上,你可以通过运行以下命令获得 C 语言编译器:
xcode-select --install
Linux 用户通常需要根据发行版(distribution)文档安装 GCC 或 Clang。比如,如果你使用 Ubuntu,可以安装 build-essential 包
Windows 的 Linux 子系统(WSL)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
检查安装是否正确
要检查是否正确安装了 Rust,打开命令行并输入:
rustc --version
应该可以看到按照以下格式显示的最新稳定版本的版本号、对应的 Commit Hash 和 Commit 日期:
rustc x.y.z (abcabcabc yyyy-mm-dd)
如果看到了这样的信息,就说明 Rust 已经安装成功了!
如果没看到,请按照下面说明的方法检查 Rust 是否在您的 %PATH% 系统变量中
在 Rust 开发环境中,所有工具都安装在
~/.cargo/bin
目录中,您可以在这里找到包括rustc
、cargo
和rustup
在内的 Rust 工具链
echo %PATH% // windows
echo $env:Path // windows PowerShell
echo $PATH // Linux or macOS
更新与卸载
通过 rustup 安装了 Rust 之后,很容易更新到最新版本,只需要在命令行中运行
rustup update
若要卸载 Rust 和 rustup ,请在命令行中运行
rustup self uninstall
本地文档
安装程序也自带一份文档的本地拷贝,可以离线阅读。运行 rustup doc 在浏览器中查看本地文档
编译和运行是单独的两步
运行Rust程序之前必须先编译,命令为:rustc 源文件名
rustc
main.rs
编译成功后,会生成一个二进制文件
在Windows上还会生成一个
.pdb
文件,里面包含调试信息
Rust 是 ahead-of-time 编译的语言
可以先编译程序,然后把可以执行文件交给别人运行(无需安装Rust)
rustc
只适合简单的 Rust 程序
开发工具
VS Code
VSCode(全称:Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器,VSCode 开发环境非常简单易用。
下载地址:https://code.visualstudio.com/
官方文档地址:https://code.visualstudio.com/docs
安装插件:
rust-analyzer:语言服务器、自动补全、语法高亮
IntelliJ Rust
下载地址:https://www.jetbrains.com/rust/
安装 Rust 插件
Clion
下载地址:https://www.jetbrains.com/clion/
安装 Rust 插件
使用 Cargo 创建项目
Cargo 是 Rust 的构建系统和包管理工具
cargo new holle_world
会创建一个新的目录 xxx (项目名也是),将会看到 Cargo 生成了两个文件和一个目录:一个 Cargo.toml
文件,一个 src
目录,一个 .gitignore
文件,以及位于 src
目录中的 main.rs
文件
Cargo.toml
TOML(Tom’s Obvious, Minimal Language) 格式,是 Cargo 的配置格式
[package] 是一个区域标题,表示下方内容是用来配置包(package)的
name,项目名
version,项目版本
authors,项目作者
edition,使用的 Rust 版本
[dependencies] 另一个区域的开始,它会列出项目的依赖项
在 Rust 里面,代码的包称作 crate
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
src
源文件
源代码都在 src
目录下
顶层目录可以放置:README、许可信息、配置文件和其它与程序源码无关的文件
如果创建项目时没有使用 cargo ,也可以把项目转化为使用 cargo:
把源代码文件移动到
src
下创建
Cargo.toml
并填写相应的配置
初始化一个新的Git仓库.gitignore
- 可以使用其它的 VCS 或不使用 VCS:
cargo new
的时候使用--vcs
这个 flag
注意:
Git 是一个常用的版本控制系统(version control system, VCS)。可以通过 – vcs 参数使 cargo new 切换到其它版本控制系统(VCS),或者不使用 VCS。运行 cargo new --help 参看可用的选项
构建并运行 Cargo 项目
cargo build
第一次运行 cargo build 会在顶层目录生成 cargo.toml
文件
- 该文件负责追踪项目依赖的精确版本
cargo run
cargo run,编译代码 + 执行结果
- 如果之前编译成功过,并且没有修改源码,那么就会直接运行二进制文件
cargo check
cargo check,检查代码,确保能通过编译,但是不产生任何可执行文件
在不生成二进制文件的情况下构建项目来检查错误
cargo check 要比 cargo build 快
- 编写代码的时候可以连续反复的使用 cargo check 检查代码,提高效率
cargo build --release
当项目最终准备好发布时,可以使用cargo build --release
来优化编译项目,会在 target/release 而不是 target/debug 生成可执行文件。如果你在测试代码的运行时间,请确保运行 cargo build – release 并使用 target/release 下的可执行文件进行测试
- 代码会运行的更快,但是编译时间更长
两种不同的配置:
- 一个开发,需要经常快速重新构建
- 一个正式发布,它们不会经常重新构建,并且希望程序运行得越快越好
猜数游戏
要创建一个新项目,进入第一章中创建的 projects 目录,使用 Cargo 新建一个项目,如下:
cargo new guessing_game
cd guessing_game
在 Cargo.toml
文件下的 [dependencies] 后添加 rand 库需要指定版本号
[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.7.3"
use std::{io, cmp::Ordering}; // prelude
use rand::Rng; // trait
fn main() {
println!("猜数!");
let secret_number = rand::thread_rng().gen_range(1..=100);
// println!("神秘数字是:{}", secret_number);
loop {
println!("猜测一个数!");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("无法读取行");
let guess:u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("你猜测的数是:{}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
变量与可变性
关键字
目前正在使用的关键字
-
as
- 强制类型转换,消除特定包含项的 trait 的歧义,或者对use
语句中的项重命名 -
async
- 返回一个Future
而不是阻塞当前线程 -
await
- 暂停执行直到Future
的结果就绪 -
break
- 立刻退出循环 -
const
- 定义常量或不变裸指针(constant raw pointer) -
continue
- 继续进入下一次循环迭代 -
crate
- 在模块路径中,代指 crate root -
dyn
- 动态分发 trait 对象 -
else
- 作为if
和if let
控制流结构的 fallback -
enum
- 定义一个枚举 -
extern
- 链接一个外部函数或变量 -
false
- 布尔字面值false
-
fn
- 定义一个函数或 函数指针类型 (function pointer type) -
for
- 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期 -
if
- 基于条件表达式的结果分支 -
impl
- 实现自有或 trait 功能 -
in
-for
循环语法的一部分 -
let
- 绑定一个变量 -
loop
- 无条件循环 -
match
- 模式匹配 -
mod
- 定义一个模块 -
move
- 使闭包获取其所捕获项的所有权 -
mut
- 表示引用、裸指针或模式绑定的可变性 -
pub
- 表示结构体字段、impl
块或模块的公有可见性 -
ref
- 通过引用绑定 -
return
- 从函数中返回 -
Self
- 定义或实现 trait 的类型的类型别名 -
self
- 表示方法本身或当前模块 -
static
- 表示全局变量或在整个程序执行期间保持其生命周期 -
struct
- 定义一个结构体 -
super
- 表示当前模块的父模块 -
trait
- 定义一个 trait -
true
- 布尔字面值true
-
type
- 定义一个类型别名或关联类型 -
union
- 定义一个 union 并且是 union 声明中唯一用到的关键字 -
unsafe
- 表示不安全的代码、函数、trait 或实现 -
use
- 引入外部空间的符号 -
where
- 表示一个约束类型的从句 -
while
- 基于一个表达式的结果判断是否进行循环
保留做将来使用的关键字
如下关键字没有任何功能,不过由 Rust 保留以备将来的应用
abstract
become
box
do
final
macro
override
priv
try
typeof
unsized
virtual
yield
声明变量使用 let 关键字
默认情况下,变量是不可变的(Immutable)
声明变量时,在变量前面加上 mut
,就可以使变量可变
fn main() {
let mut x = 5;
println!("The value of x is {}", x);
x = 6;
println!("The value of x is {}", x);
}
常量
static:具有 ‘static 生命周期的,可以是可变的变量(须使用 static mut
关键字)
有个特例就是 “string” 字面量。它可以不经改动就被赋给一个 static 变量,因为它 的类型标记:&’static str 就包含了所要求的生命周期 ‘static。其他的引用类型都 必须特地声明,使之拥有’static 生命周期。
常量(constant),常量在绑定值以后也是不可变的,但是它与不可变的变量有很多区别:
不可以使用
mut
,常量永远都是不可变的声明常量使用
const
关键字,它的类型必须被标注常量可以在任何作用域内进行声明,包括全局作用域
常量只可以绑定到常量表达式,无法绑定到函数的调用结果或只能在运行时才能计算出的值
在程序运行时间,常量在其声明的作用域内一直有效
命名规范:Rust 里常量使用全大写字母,每个单词之间用下划线分开,
- 例子:
const MAX_POINTS:u32 = 100_000;
Shadowing(隐藏)
可以使用相同的名字声明新的变量,新的变量就会 shadow(隐藏)之前声明的同名变量
- 在后续的代码中这个变量名代表的就是新的变量
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}");
}
println!("The value of x is: {x}");
}
cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6
shadow 和把变量标记为 mut
是不一样的:
如果不使用 let 关键字,那么重新给非 mut
的变量赋值会导致编译时错误
而使用 let 声明的同名新变量,也是不可变的
使用 let 声明的同名新变量,它的类型可以与之前不同
fn main() {
let spaces = " ";
let spaces = spaces.len();
println!("{}", spaces);
}