rustup:是 Rust 的安装程序,管理 Rust 版本和相关工具的命令行工具- 更新 rust:rustup update
- 卸载 rust:rustup self uninstall
rustc:是 Rust 编程语言的编译器- 检查是否安装了 rust:
rustc --version
- 检查是否安装了 rust:
cargo:是 Rust 的构建系统和包管理器- 检查是否安装了 cargo:
cargo --version - 使用 cargo 创建项目:
cargo new hello_world cargo build: 构建项目cargo run:构建并运行项目cargo check:在不生成二进制文件的情况下构建项目来检查错误cargo build --release优化编译项目cargo update# 更新所有依赖cargo update -p regex# 只更新 “regex”
- 检查是否安装了 cargo:
创建项目
我们使用 cargo 来创建一个 hello_world 项目
$ cargo new hello_world
$ cd hello_world
可以看到项目的结构和配置文件都是由 cargo 生成:
$ tree
.
├── .git
├── .gitignore
├── Cargo.toml
└── src
└── main.rs
Cargo.toml,是 Cargo 使用的配置文件,其中包含了项目的名称、项目的版本、使用的 Rust 版本以及项目依赖:
代码内容很简单,就是打印一句 “Hello, world!” , ./src/main.rs:
- 手动编译和运行项目
cargo run,直接运行项目
在 hello_world 目录下运行 cargo run, 此命令对项目进行编译,然后再运行,等同于第一种方法中的两个指令。
语法总结
变量
- 变量绑定
let a = "hello world",rust 中给变量 a 赋值的这种方式叫作“变量绑定”,因为 rust 中有个“所有权” 的概念。
- 变量可变性
rust 中的变量,默认是不可变的,可以通过 mut 关键字来实现变量的可变性。例如:
fn main() {
let x = 1;
println!("x 的值是: {}",x);
x = 2;
println!("x 的值是: {}",x);
}
以上再次给 x 绑定值时会发生错误,因为它是不可变的,正确的应该是:
fn main() {
let mut x = 1;
println!("x 的值是: {}",x); // x 的值是: 1
x = 2;
println!("x 的值是: {}",x); // x 的值是: 2
}
- 常量
常量的值也是不可修改,但是和变量有些差异:
- 常量不使用
mut - 常量使用
const关键字来声明 - 常量值的类型必须标注
- 变量遮蔽(shadowing)
在 rust 中,我们可以使用 let 多次声明一个相同的变量名,后声明的会遮蔽之前所声明的。例如:
let x = 1;
let x = x +1;
println!("x 的值是: {}",x); // x 的值是: 2
遮蔽与使用 mut 的区别是:
- 如果忘记使用 let 来再次声明,则会导致编译时错误。
- 遮蔽其实是创建了一个同名的新变量,这个变量的类型可以与之前不同。
- 未使用变量
rust 中,如果创建了一个变量,但是没有使用到它,那么会产生一个警告,我们可以使用下划线作为变量名的开头,来消除警告:
let _x = 1;
- 变量解构
let 表达式还可以进行变量的解构,例如:
// a = true,不可变; b = false,可变
let (a, mut b): (bool,bool) = (true, false);
println!("a = {:?}, b = {:?}", a, b); //a = true, b = false
b = true;
println!("b = {:?}", b); //b = true
解构式赋值
在 Rust 1.59 版本后,可以在赋值语句的左式中使用元组、切片和结构体模式了。
struct Struct {
e: i32
}
fn main() {
let (a, b, c, d, e);
(a, b) = (1, 2);
// _ 代表匹配一个值,但是我们不关心具体的值是什么,因此没有使用一个变量名而是使用了 _
[c, .., d, _] = [1, 2, 3, 4, 5];
Struct { e, .. } = Struct { e: 5 };
assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
}
这里
[c, .., d, _]是一个模式,表示要匹配的数组的结构。其中c是数组的第一个元素,..表示中间的所有元素(这些元素我们不关心,所以用..来表示),d是数组的倒数第二个元素,_是数组的最后一个元素。
这种解构赋值的方式会将数组的元素分别赋值给c,d和_。所以在这个例子中,c的值会是1,d的值会是4,而_的值会是5。
数据类型
rust 是 静态类型 语言,每一个值都有自己的数据类型。
数值类型
- 整型:
| 长度 | 有符号类型 | 无符号类型 |
|---|---|---|
| 8 位 | i8 | u8 |
| 16 位 | i16 | u16 |
| 32 位 | i32 | u32 |
| 64 位 | i64 | u64 |
| 128 位 | i128 | u128 |
| 视架构而定 | isize | usize |
isize和usize类型取决于程序运行的计算机 CPU 类型: 若 CPU 是 32 位的,则这两个类型是 32 位的,同理,若 CPU 是 64 位,则是 64 位。- rust 整型默认使用
i32。
- 浮点型
rust 的浮点数类型是 f32 和 f64,分别占 32 位和 64 位。默认类型是 f64,所有的浮点型都是有符号的。
- 序列(Range)
序列用来生成连续的数值,例如 1..5,生成从 1 到 4 的连续数字,不包含 5 ;1..=5,生成从 1 到 5 的连续数字,包含 5。序列只允许用于数字或字符类型,它的用途很简单,常常用于循环中:
for i in 'a'..='z' {
println!("{}",i);
}
字符类型(char)
- rust 的字符不仅仅是
ASCII,所有的Unicode值都可以作为 Rust 字符,包括单个的中文、日文、韩文、emoji 表情符号等等。 - 由于
Unicode都是 4 个字节编码,因此字符类型也是占用 4 个字节。 - rust 的字符用
''来表示,""来表示字符串。
布尔
- rust 中的布尔类型有两个可能的值:
true和false - 布尔值占用内存的大小为
1个字节。
单元类型
- 单元类型就是
(),例如fn main()这个 main 函数返回的就是单元类型()。 - 可以用
()作为map的值,表示不关注具体的值,只关注key。 这种用法和 Go 语言的struct{}类似,可以作为一个值用来占位,但是完全不占用任何内存。
函数
- 函数声明
- rust 中使用
fn关键字来声明函数。 - 函数和变量名采用 snake case 规范,所有字幕都是小写且使用下划线分割单词。
- 每个函数参数都需要标注类型。
例如:
fn add(i: i32, j: i32) -> i32 {
i + j
}
上面代码中没有使用 return 关键字来返回值。其中的 i + j 没有以分号结尾,是一条表达式,求值后,返回一个值。在 rust 中,函数的返回值等同于函数体最后一个表达式的值。
- 语句和表达式
- 语句:执行一些操作但不返回值的指令。
- 表达式:计算并产生一个值。
数调用是一个表达式。宏调用是一个表达式。用大括号创建的一个新的块作用域也是一个表达式,例如:
fn main() {
let y = {
let x = 1;
x + 2
};
println!("y 的值为: {y}"); // y 的值为: 3
}
x + 2这一行没有分号,最后的值返回给 y。表达式的结尾没有分号,如有加上了分号就变成了语句。语句不会返回值。
- 无返回值
()
单元类型 (),是一个零长度的元组。可以用来表达一个函数没有返回值:
- 函数没有返回值,那么返回一个
() - 通过
;结尾的表达式返回一个()
- 发散函数
!
发散函数(diverging function)通常用来表示永远不会返回的函数。发散函数的返回类型是 !,它不是任何其他类型的子类型,因此无法从发散函数返回任何值。
发散函数的使用场景:
- 无限循环的函数
fn game_loop() -> ! {
loop {
// ...
}
}
- 彻底退出程序
fn exit_program() -> ! {
std::process::exit(0);
}
- 当出现不可恢复的错误时
fn unrecoverable_error() -> ! {
panic!("Unrecoverable error occurred");
}
控制流
if 表达式
- if...else if...else...
let number = 7;
if number < 5 {
println!("Number is less than 5");
} else if number == 5 {
println!("Number is equal to 5");
} else {
println!("Number is greater than 5");
}
if语句块是表达式,条件 必须 是bool值
- 在 let 语句中使用 if:
let condition = true;
let number = if condition {
1
} else {
2
};
- 用
if来赋值时,要保证每个分支返回的类型一样,如果返回类型不一致就会报错
循环
- loop
loop 就是一个简单的无限循环,可以在内部实现逻辑通过 break 关键字来控制循环何时结束。
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 5 {
break counter * 2;
}
};
println!("The result is {}", result); // The result is 10
}
- break 可以单独使用,也可以带一个返回值,有些类似
return。 - loop 是一个表达式,因此可以返回一个值。
- while
while 需要一个条件来循环,当该条件为 true 时,继续循环,条件为 false,跳出循环,这种结构消除了很多使用 loop、if、else 和 break 时所必须的嵌套,这样更加清晰。
fn main() {
let mut counter = 0;
while counter != 5 {
counter = counter + 1;
}
println!("The counter is {}", counter); // The counter is 5
}
- for
可以使用 for 循环来访问一个集合中的每个元素:
fn main() {
let a = [1, 2, 3];
for v in a {
println!("the v is: {v}");
}
}
//the v is: 1
//the v is: 2
//the v is: 3