1. 构建运行环境
我们在Rust环境配置和入门指南中详细介绍了
- 如何安装Rust环境
- 构建一个Rust应用
- 编译和运行的区别
- 使用Cargo构建Rust应用
下面,我们就之间直入主题了。
通过创建一个名为 main.rs 的文件并将以下代码放入其中来编写我们的第一个 Rust 代码:
然后通过运行 rustc main.rs 和 ./main.exe 来运行这个程序,就像运行 C 程序一样。
Cargo 是 Rust 的构建系统和包管理器
我们也可以使用 cargo 创建项目。
- cargo new hello_cargo:初始化一个新项目。
- cargo build:构建一个 cargo 项目。
- cargo run:运行一个 cargo 项目,这将编译并运行代码。
- cargo check:检查是否有编译错误,它比cargo build速度更快。
- cargo build --release:这将使用优化进行编译,用于最终生产构建。
2. 变量类型
在 Rust 中,默认情况下「变量是不可变」的,这意味着一旦给变量赋值,其值就不会改变。
所以如果想要一个可变的,即可改变的值,使用 mut。
- 整数:有各种大小的有符号和无符号整数(例如,i8、i16、i32、i64、u8、u16、u32、u64)
图片
- 浮点数:单精度和双精度浮点数(例如,f32、f64)
图片
- 布尔值:Rust的布尔类型只拥有两个可能的值true和false,它「只会占据单个字节的空间大小」。使用bool来表示一个布尔类型。
- 字符:在Rust中char类型「占4字节」,是一个Unicode标量值,这意味着它可以表示比ASCII多的字符内容。使用char 类型表示一个字符类型
- 字符串:可变字符串
图片
- 字符串切片:不可变且借用的字符串切片
- 数组:数组中每一个元素都必须是「相同类型」。 Rust中「数组拥有固定的长度,一旦声明就再也不能随意更改大小」
- 元组
图片
为了从元组中获得单个的值,可以使用「模式匹配」来解构元组
还可以通过「索引」并使用点号(.)来访问元组中的值
- 向量
图片
- 指针和引用
指针是一个变量,它存储了一个值的「内存地址」
Rust 中最常见的指针是引用。引用以 & 符号为标志并「借用了它们所指向的值」。除了引用数据没有任何其他特殊功能。它们也没有任何额外开销,所以应用得最多。
以上内容就是Rust中所涉及到的各种数据类型,我们可以从以下的链接中找到更为详细的解释
- 基础概念
- 集合
- 智能指针
3. 操作数组
不可变数组:
不可变数组在 Rust 中用 [T; N] 语法来声明,其中 T 表示数组元素的类型,而 N 表示数组的长度。
对于不可变数组,我们可以使用下标访问其元素,但不能修改元素的值。
可变数组
Vec<T> 是 Rust 中可变长数组的实现,它允许您动态地增加或减少数组的大小。
4. 操作字符串
5. 操作向量
6. 函数
Rust代码使用「蛇形命名法」来作为规范函数和变量名称的风格。蛇形命名法「只使用小写的字母进行命名,并以下画线分隔单词」。
- 参数,它们是一种「特殊的变量,并被视作函数签名的一部分」。当函数存在参数时,你需要在「调用函数时为这些变量提供具体的值」
- 在Rust中,「函数的返回值等同于函数体的最后一个表达式」。
语法
最后一行返回值时不需要调用 return。
如果想要一个无返回值的函数,不要定义返回类型。
我们可以在基础概念_函数部分查看更详细的解释
7. 输入/输出
输入
要读取一个值,使用 io stdin 并给出变量的值,在失败时需要提供 expect 消息,否则会出错。
输出 / 打印
你也可以在末尾有变量
8. Shadowing
在Rust中,一个「新的声明变量可以覆盖掉旧的同名变量」,我们把这一个现象描述为:「第一个变量被第二个变量遮蔽Shadow了」。这意味着随后使用这个名称时,它指向的将会是第二个变量。
图片
9. 控制块
图片
If else
10. 循环
Rust提供了3种循环
- loop
- while
- for
loop
While 循环
For 循环
foreach
当然也少不了对数值的遍历操作。
..:它表示一个扩展运算符,表示从第一个数字到最后一个数字生成。
我们也可以在循环中使用 continue 和 break。
11. 所有权
图片
这个概念是需要特别注意和反复观看的部分。
MOVE(或)重新分配变量
当变量值被重新分配时,值会给新的所有者,并且旧的所有者被丢弃。
这种行为在字符串中经常看到,而不是其他类型,如下所示:
这将导致错误,因为 s1 在 s2=s1 之后不再有效。
如何解决上面的问题呢,我们可以使用 Clone:
某些类型隐式实现了 Clone。
例如,整数隐式实现了 Clone,因此这段代码不会报错。
图片
所有权和函数
如果我们像在变量被移动后,继续使用,那么我们就使用 takes_ownership(s.clone()); (或者)在 takes_ownership 函数中返回值,像这样:
借用 — 所有权
传递变量的引用,所有权不会被传递。
我们称「创建引用的操作为借用」。就像现实生活中,如果一个人拥有一样东西,你可以从他们那里借来。借了之后,你必须归还。你不拥有它。
针对此处更详细的内容,可以翻看我们之前的所有权
12. 结构体
struct,或者 structure,是一个「自定义数据类型」,允许我们命名和包装多个相关的值,从而形成一个有意义的组合。
图片
在 user2 中,你会看到 ..,它是扩展运算符,将 user1 中剩余的值传递给 user2(除了已经定义的 email)。
结构体的方法
使用 impl 结构体名,并在其中定义函数。
图片
针对此处更详细的内容,可以翻看我们之前的结构体
13. 枚举
枚举,也被称作 enums。枚举允许你通过「列举可能的成员variants来定义一个类型」
枚举的成员位于其标识符的「命名空间中」,并「使用两个冒号分开」。
match
这是类似于 switch 的东西,
每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 match 表达式的返回值。
Option 枚举和其相对于空值的优势
图片
if Let
这是一种使用 if 的花式方式,我们在其中定义一个表达式。
14. 并发性
并发编程和并行编程
图片
代码实现
为了创建一个新线程,需要调用 thread::spawn 函数并「传递一个闭包」,并在其中包含希望在新线程运行的代码。
可以通过将 thread::spawn 的「返回值储存在变量中来修复新建线程部分没有执行或者完全没有执行的问题」。thread::spawn 的返回值类型是 JoinHandle。JoinHandle 是一个「拥有所有权的值」,当「对其调用 join 方法时,它会等待其线程结束」。
thread::spawn 要求闭包具有 'static 生命周期,这意味着它不会从周围范围借用任何东西,并且可以在整个程序的持续时间内存在。
因此,我们使用move 闭包,其经常与 thread::spawn 一起使用,因为它允许我们「在一个线程中使用另一个线程的数据」。