Rust入门总结

[toc]

写在前面

本文主要是一个go开发者,入门rust学习的一点总结,都是基础语法相关的,会从基础语法,所有权,借用,可变借用,trait,智能指针和异步编程等知识点的介绍,最后通过三个实战小demo(web开发,cli命令行工具开发和task任务开发)进行校验学习成果

rust安装

工欲善其事,必先利其器。先看如何快速安装rust,我们安装一个rust版本管理工具,当然你也可以直接安装rust,我这里以Linux为例

rustup

rustup是rust的一个版本管理工具,可以帮助我们安装和切换到不同的rust版本,使用以下命令安装工具链


curl  --proto  '=https'  --tlsv1.2  -sSf  https://sh.rustup.rs | sh

按提示选择默认安装选项(推荐选项 1)

安装完成后,执行以下命令:


source  $HOME/.cargo/env

可以使用以下命令验证是否安装成功


> rustup --version

rustup  1.28.1 (f9edccde0 2025-03-05)

info:  This  is  the  version  for  the  rustup  toolchain  manager,  not  the  rustc  compiler.

info:  The  currently  active  `rustc`  version  is  `rustc  1.86.0 (05f9846f8 2025-03-31)`

rust安装

下面我们来正式安装rust


# 安装指定版本(例如 1.65.0 和 1.64.0)

rustup  install  1.65.0

rustup  install  1.64.0

# 查看已安装版本

rustup  show

# 临时切换版本

rustup  run  1.65.0  rustc  --version

# 设置默认版本

rustup  default  1.65.0

验证rust是否安装成功:


> rustc --version

rustc  1.86.0 (05f9846f8 2025-03-31)

验证cargo是否被成功安装

cargo是rust的包管理工具,你暂时可以理解为go的mod功能


> cargo --version

cargo  1.86.0 (adf9b6ad1 2025-02-28)

第一个rust程序

老规矩,我们先来写一个hello,rust程序


vim  main.rs

写入一下内容:


fn  main() {

println!("hello, rust!"); //println!是rust中的宏,暂时理解打印即可

}

然后运行以下进行编译


rustc  main.rs

然后运行这个二进制文件就可以了


> ls

main  main.rs

> ./main

hello,  rust!

基础语法

下面是rust的基础语法,可以看下面这个图:


mindmap

root((Rust基础语法))

%% 基础类型分支

Rust的基础类型

赋值语句

let关键字

数字类型

整数

整数字面量的辅助写法

浮点数

f32和f64

布尔类型

truefalse

字符

char,Unicode散列值

字符串

String,UTF8编码

反斜杠转义

禁止转义的字符串字面量

r""或r#""#

字节串

b"字符串"

数组

array类型

存储同一类型的多个值

固定长度

动态数组

Vec向量

存储同一类型的多个值

容量可变

哈希表

存储key-value映射关系

%% 复合类型分支

复合类型

元组

固定长度的列表

结构体

struct关键字

积类型

枚举

enum关键字

和类型

%% 控制流分支

控制流

分支语句

if else构造分支

支持表达式返回

循环语句

loop

无条件循环

while

条件判断循环

for

迭代器的遍历

%% 函数和模块分支

函数和模块

函数

fn关键字

形参与实参

栈帧

闭包

两个竖线符号||

可以捕获函数中的局部变量

模块

模块系统

基础数据类型

和其他语言一样,该有的基础数据类型都有,这里我们简单过一下

标量类型


fn  main() {

// -------------------------------

// 1. 标量类型 (Scalar Types)

// -------------------------------

// 整数类型 - 默认推导为 i32

let  integer: i32 = -42; // 有符号整数

let  unsigned: u32 = 1024; // 无符号整数

// 浮点类型 - 默认推导为 f64

let  float: f64 = 3.14159; // 双精度浮点

// 布尔类型

let  is_rust_cool: bool = true;

// 字符类型 (Unicode 标量)

let  emoji: char = '🚀';

let  x: i32 = 100;

x = 101;

// -------------------------------

// 打印所有值

// -------------------------------

println!("整数: {} (有符号), {} (无符号)", integer, unsigned);

println!("浮点数: {:.2} (保留两位小数)", float); // 格式化输出

println!("布尔值: {}", is_rust_cool);

println!("字符: {}", emoji);

}

输出如下:


> ./main

整数: -42 (有符号), 1024 (无符号)

浮点数: 3.14 (保留两位小数)

布尔值: true

字符: 🚀

这里需要注意的几个点:

  • let: rust变量的声明必须使用let开头,然后是变量名称,类型,末尾需要使用;结束,像下面这种

let  integer: i32 = -42;
  • mut 表示可变的意思,如果你需要改变某一个变量的值,我们在声明时就需要这样声明

let  mut  x: i32 = 100;

x = 101;

这样做有什么好处吗?当然的rust是一门非常严谨的语言,但我们在看到mut需要注意了,说明后续这个变量一定会被使用到并且做值的改变,这在系统开发中,可以对开发者起到一个紧觉的作用

复合类型


fn  main() {

// -------------------------------

// 2. 复合类型 (Compound Types)

// -------------------------------

// 元组 (Tuple) - 固定长度不同类型

let  tuple: (i32, f64, char) = (500, 6.28, 'θ');

// 数组 (Array) - 固定长度相同类型

let  array: [i32; 5] = [1, 2, 3, 4, 5];

// 元组解构和访问

let (x, y, z) = tuple;

println!("元组解构: x={}, y={}, z={}", x, y, z);

println!("元组索引: 索引1 = {}", tuple.1);

println!("数组: {:?}", array); // 使用 Debug trait 打印

println!("数组首元素: {}", array[0]); // 索引访问

}

输出:


> ./main

元组解构: x=500, y=6.28, z=θ

元组索引: 索引1 = 6.28

数组: [1, 2, 3, 4, 5]

数组首元素: 1

字符串类型


fn  main() {

// -------------------------------

// 3. 字符串类型

// -------------------------------

// &str:字符串切片(不可变视图)

let  greeting: &str = "Hello, Rustaceans!";

// String:可增长的堆分配字符串

let  mut  message = String::from("Learning");

message.push_str(" Rust!"); // 修改字符串

// 元组解构和访问

let (x, y, z) = tuple;

println!("元组解构: x={}, y={}, z={}", x, y, z);

println!("元组索引: 索引1 = {}", tuple.1);

println!("数组: {:?}", array); // 使用 Debug trait 打印

println!("数组首元素: {}", array[0]); // 索引访问

println!("字符串切片: {}", greeting);

println!("可变字符串: {}", message);

}

输出:


> ./main

元组解构: x=500, y=6.28, z=θ

元组索引: 索引1 = 6.28

数组: [1, 2, 3, 4, 5]

数组首元素: 1

字符串切片: Hello, Rustaceans!

可变字符串: Learning Rust!

动态数组Vec

动态数组的开发中是无处不在,我们来看看动态数组, 直接看示例


fn  main() {

let  mut  subject_list = vec![]; //声明一个vec, 这里可以不指明数据类型,在具体使用时,编译器会自动推断

subject_list.push("math");

println!("subject_list: {:?}", subject_list);

let  mut  vector: Vec<i32> = vec![1, 2, 4, 8];

vector.push(16);

vector.push(32);

vector.push(64);

println!("{:?}", vector);

let  mut  member_list: Vec<&str> = vec!["iceymoss", "kuk", "taks"];

member_list.push("lak"); // push元素

member_list.remove(0); // 移除索引为0的元素

println!("{:?}", member_list);

println!("{}", member_list[2]); // 索引访问

}

哈希表

哈希表这种东西,在go里面我们我们知道是map这种数据类型,来看看rust中的


use  std::collections::HashMap; //哈希表在标准库中,需要手动导入,use关键字是rust导入方式

fn  main() {

let  mut  age = HashMap::new(); //声明一个哈希表,未指定类型, 在使用时自动推断类型

age.insert("iceymoss", 18);

println!("{:?}", age.get("iceymoss"));

let  mut  year: HashMap<i32, String> = HashMap::new();

year.insert(2000, "龙".to_string()); // 字符串切片转为String

year.insert(2025, "蛇".to_string());

println!("year: {:?}", year);

let  mut  pass: HashMap<&str, bool> = HashMap::new();

pass.insert("iceymoss", true);

pass.insert("lass", false);

pass.remove("lass");

println!("pass: {:?}", pass);

println!("len: {}", pass.len());

println!("key: {:?}", pass.keys());

println!("value: {:?}", pass.values());

}

输出:


> ./main

Some(18)

year: {2025: "蛇", 2000: "龙"}

pass: {"iceymoss": true}

len: 1

key: ["iceymoss"]

value: [true]

复合数据类型

在rust中复合数据类似主要是:元组,结构体和枚举

元组

如何理解元组呢, 在go中没有元组这个东西啊,我是将其理解为大杂烩,啥都可以放进来,主要特定如下:

  • 固定长度:创建后元素数量不可改变

  • 异构:元素可以是不同类型

  • 匿名字段:通过索引位置而非名称访问

  • 零成本抽象:编译时确定类型,运行时无开销

来看一个简单示例:


fn  main() {

let  tuple: (i32, f32, bool) = (23, 32.4, true);

let  i: i32 = tuple.0;

let  f: f32 = tuple.1;

let  x: bool = tuple.2;

println!("{}", i);

println!("{}", f);

println!("{}", x);

let  tuple1: (i32, f32, bool) = (23, 32.4, true);

// 直接解构

let (a, b, c) = tuple;

println!("{}", a);

println!("{}", b);

println!("{}", c);

}

结构体

结构体那可太有用了,可以用来封装各种功能,在rus中使用struct关键字来定义,直接看示例:


// 定义一个Person结构体

struct  Person {

name: String,

age: i8

}

fn  main() {

let  p = Person { // 注意:在rust中必须所有字段绑定值的,不然编译器是无法通过的,这也是rust非常严谨的一个体现

name:String::from("iceymoss"), //String在堆空间中

age:18,

};

println!("person: name={},age={}", p.name, p.age); //结构体字段使用.进行访问

}

输出:


> ./main

person: name=iceymoss,age=18

再来看一个复杂的结构体,结构体嵌套:


// 定义一个坐标

struct  Point {

x: f32,

y: f32,

}

// 定义一个居住信息

struct  Live {

name: String,

point: Point,

}

// 定义一个个人信息

struct  Person {

name: String,

age: u8,

likes: u8,

gender: u8,

live: Live

}

fn  main() {

// 实例化一个Person

let  ming: Person = Person {

name: "iceymoss".to_string(),

age: 20,

likes: 5,

gender: 1,

live: Live { // 实例化

name: "ShangHai".to_string(),

point: Point { x: 123.32, y: 38.2374 }, // 实例化

}

};

println!("ming.name = {}", ming.name);

println!("ming.age = {}", ming.age);

println!("ming.likes = {}", ming.likes);

println!("ming.gender = {}", ming.gender);

println!("ming.live.name = {}", ming.live.name);

println!("ming.points.x = {}", ming.live.point.x);

println!("ming.points.y = {}", ming.live.point.y);

}

输出:


> ./struct_demo

ming.name = iceymoss

ming.age = 20

ming.likes = 5

ming.gender = 1

ming.live.name = ShangHai

ming.points.x = 123.32

ming.points.y = 38.2374

枚举

枚举在rust中真的是无处不见,大名鼎鼎的Result<T, E>和Option,先来看看枚举如何使用:


#[derive(Debug)]

enum  Book {

Papery, // 纸质书

Electronic // 电子书

}

fn  main() {

let  book = Book::Papery; // 声明一个枚举

println!("{:?}", book);

}

这里表示定义了书的枚举,其中有两个变体,纸质书和电子书,输出:


> ./enum_demo

Papery

没错枚举就是这么简单,上强度, 在枚举中还可以包含字段,看示例:


enum  Book {

Papery {index: u32}, // 变体中定义了u32类型的一个索引,表示书的页号

Electronic {url: String}, // 变体中定义了一个String类型的url,表示电子书的链接

}

fn  main() {

let  book = Book::Papery{index: 1001};

let  ebook = Book::Electronic{url: String::from("http://mybook.com/electronic/bookname/index.html")};

// match 枚举类实例 {

// 分类1 => 返回值表达式,

// 分类2 => 返回值表达式,

// ...

// }

// match 枚举类实例 {

// 分类1 => {执行的代码块},

// 分类2 => {执行的代码块},

// ...

// }

match  book {

Book::Papery { index } => {

println!("Papery book {}", index);

},

Book::Electronic { url } => {

println!("E-book {}", url);

}

}

match  ebook {

Book::Electronic { url } => {

println!("E-book {}", url);

}

Book::Papery { index } => {

println!("Papery book {}", index);

}

}

}

先来看看输出:


> ./main

Papery book 1001

E-book http://mybook.com/electronic/bookname/index.html

我们可以看到match,没错他就是rust中大名鼎鼎的匹配模式,我们来看看Option是怎么使用的:


// Option是标准库中的

// enum Option<T> {

// Some(T),

// None,

// }

fn  main() {

let  opt = Option::Some("Hello");

match  opt {

Option::Some(something) => {

println!("{}", something);

},

Option::None => {

println!("opt is nothing");

}

}

let  opt1: Option<&str> = Option::None;

match  opt1 {

Option::Some(something) => {

println!("{}", something);

},

Option::None => {

println!("opt is nothing");

}

}

// 由于Option是rust内部提供的,所以支持直接简化写法

let  t = Some(64);

match  t {

Some(64) => println!("Yes"),

_ => println!("No"), // 注意,枚举在匹配模式中,需要列出所有变体,如果不想匹配所有变体,使用_进行处理

}

//if let 语法

let  i = 1;

match  i {

0 => println!("zero"),

_ => {

println!("unkown i");

},

}

}

控制流

条件语句

和所有编程语言一样,也就是if - else if - else, rust的条件语句风格其实和go非常相似,直接来看示例:


fn  main() {

let  number = 6;

if  number % 4 == 0 {

println!("number is divisible by 4");

} else  if  number % 3 == 0 {

println!("number is divisible by 3");

} else  if  number % 2 == 0 {

println!("number is divisible by 2");

} else {

println!("number is not divisible by 4, 3, or 2");

}

}

看上去是不是中规中矩,接着看示例,使用代码块:


fn  main() {

let  condition = true;

let  number = if  condition {

5

} else {

6

};

println!("The value of number is: {}", number);

}

if分支还可以做这种表达式,看上去是不是很像三元表达式, rust的条件语句非常简单

while

我们知道在go中其实是没有while关键字的,看示例:


fn  main() {

let  mut  x: i32 = 100;

while  x != 0 {

println!("value: {}", x);

x -= 1;

}

let  arr:[i32; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let  mut  index: i32 = 0;

while  index < arr.len() as  i32 {

println!("value: {}", arr[index  as  usize]);

index += 1;

}

}

输出:


> ./demo4

value: 100

value: 99

value: 98

value: 97

value: 96

value: 95

value: 94

value: 93

value: 92

value: 91

value: 90

value: 89

value: 88

value: 87

value: 86

value: 85

value: 84

value: 83

value: 82

value: 81

value: 80

value: 79

value: 78

value: 77

value: 76

value: 75

value: 74

value: 73

value: 72

value: 71

value: 70

value: 69

value: 68

value: 67

value: 66

value: 65

value: 64

value: 63

value: 62

value: 61

value: 60

value: 59

value: 58

value: 57

value: 56

value: 55

value: 54

value: 53

value: 52

value: 51

value: 50

value: 49

value: 48

value: 47

value: 46

value: 45

value: 44

value: 43

value: 42

value: 41

value: 40

value: 39

value: 38

value: 37

value: 36

value: 35

value: 34

value: 33

value: 32

value: 31

value: 30

value: 29

value: 28

value: 27

value: 26

value: 25

value: 24

value: 23

value: 22

value: 21

value: 20

value: 19

value: 18

value: 17

value: 16

value: 15

value: 14

value: 13

value: 12

value: 11

value: 10

value: 9

value: 8

value: 7

value: 6

value: 5

value: 4

value: 3

value: 2

value: 1

value: 1

value: 2

value: 3

value: 4

value: 5

value: 6

value: 7

value: 8

value: 9

value: 10

loop

loop就是无限循环的意思,可以理解为go的for{...}看看示例:


fn  main() {

let  mut  x: i32 = 0; //mut 表示可以变

loop {

x = x + 1;

if  x == 50 { //等于50时退出循环

break;

}

println!("{}", x);

}

}

迭代器(for)

对于遍历某一些数据结构,我们可能使用迭代器会更方便,我们来读取数据,例如在go中我们常用for i, v := range list {...},下面我们来看看rust的迭代器,这需要注意的是,会涉及到rust的所有权三态(所有权变量,不可变引用变量和可变引用变量),先来看示例:


fn  main() {

for  i  in  1..=1000 { //迭代1000次, 注意这里的边界问题,[1,1000], 如果是0..=1000那就是[0,1000]了

if  i > 101 { //退出

break;

}

println!("{}", i);

}

for  j  in  'a'..='z' { // 从a迭代到z,注意边界,[a,z]

if  j == 'y' {

break;

}

println!("{}", j);

}

}
所有权移动迭代

语法是这样的for i in list 这样迭代的话,list中的每一个元素随着迭代进行,他们的所以权会自动到i上,看示例:


fn  main() {

let  mut  list = vec![10, 2, 111, 34, 12, 43];

for  i  in  list { // 会移动所有权

println!("{}", i);

}

}

输出:


10

2

111

34

12

43

这样看着没有问题,但是如果我们想继续使用list,看看会发送什么?


fn  main() {

let  mut  list = vec![10, 2, 111, 34, 12, 43];

for  i  in  list { // 会移动所有权

println!("{}", i);

}

let  l = list; // 注意这里

}

当我们编译时会发现报错:


> rustc range.rs

error[E0382]: use of moved value: `list`

--> range.rs:44:14

|

19 | let list = vec![10, 2, 111, 34, 12, 43];

| ---- move occurs because `list` has type `Vec<i32>`, which does not implement the `Copy` trait

...

39 | for i in list { // 会移动所有权

| ---- `list` moved due to this implicit call to `.into_iter()`

...

44 | let _l = list;

| ^^^^ value used here after move

|

note: `into_iter` takes ownership of the receiver `self`, which moves `list`

--> /home/jeff/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:313:18

|

313 | fn into_iter(self) -> Self::IntoIter;

| ^^^^

help: consider iterating over a slice of the `Vec<i32>`'s content to avoid moving into the `for` loop

|

39 | for i in &list { // 会移动所有权

| +

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0382`.

这里的提示就是说list的所有权发送了移动,list失去了对堆空间值的所有权,他会被回收掉,所以不能再使用了。

不可变引用迭代

语法是这样的,for i in &list 多了一个&符号,示例:


fn  main() {

let  mut  list = vec![10, 2, 111, 34, 12, 43];

for  i  in &list { // 不可变借用,开始借用list

println!("{}", i);

}

//这里归还借用

let  l = list;

}

当我们使用不可变引用时,后续迭代器归还借用后,就可以继续使用list了

可变引用

语法就是加了一个mut关键字,for i in &mut list,&引用,mut可变,示例:


fn  main() {

let  mut  list = vec![10, 2, 111, 34, 12, 43];

for  i  in &mut  list { // 可变借用

if *i == 2 {

*i = 1000 //可变借用就可以改变值了

}

}

}

函数

函数使用fn进行定义,->表示返回值,示例:


fn  add(x: i32, y: i32) -> i32 { //有返回值

x + y // 等同于 return x + y

}

fn  print(str: &str) { // 没有返回值

println!("{}", str);

}

fn  main() {

let  ans = add(10, 20);

println!("add: {} + {} = {}", 10, 20, ans);

print("hello, iceymoss");

}

再看一些复杂的示例:


// 函数体中的表达式与语句

fn  check_even(num: i32) -> bool {

// 使用表达式返回值 (无 return 关键字)

num % 2 == 0

}

fn  print_result(num: i32) {

// 使用语句执行操作(无返回值)

println!("数字 {} 是{}数",

num,

if  check_even(num) { "偶" } else { "奇" }

);

}

看看Result和Option的用法:


// 使用 Option 和 Result 处理错误

fn  find_item(items: &[i32], target: i32) -> Option<usize> {

for (index, &item) in  items.iter().enumerate() {

if  item == target {

return  Some(index);

}

}

None

}

fn  safe_divide(a: i32, b: i32) -> Result<i32, String> {

if  b == 0 {

Err("除以零错误".to_string())

} else {

Ok(a / b)

}

}

方法

我们在go中都知道方法这个概念,rust本身不是面向对象的语言,但是我们也可以为其添加方法,这一点我觉得和go很像,为一个结构体实现方法使用关键字impl, 下面来看示例,我们定义一个矩形,结构体,为其实现了几个方法:


#[derive(Debug)] // rust使用这种注解的方式来为结构体添加约束或者实现某一个trait, 后续会说到trait这个概念

struct  Rectangle {

width: u32,

height: u32,

}

impl  Rectangle {

fn  create(width: u32, height: u32) -> Rectangle { //这种在impl Rectangle 作用域的但是没有传入self我们管叫关联函数,他不是这个结构体的方法,他类似go的工厂函数,cpp的构造函数

Rectangle { width, height }

}

fn  area(&self) -> u32 {

self.width * self.height

}

// 可以看到&self这里其实就是语法糖,你可以理解为以下写法:

// fn area(rectangle: &Rectangle) -> u32 {

// self.width * self.height

// }

fn  perimeter(&self) -> u32 {

(self.width + self.height) * 2

}

fn  heighter(&self, rect: &Rectangle) -> bool {

self.height > rect.height

}

fn  wider(&self, rect: &Rectangle) -> bool {

self.width > rect.width

}

}

fn  main() {

let  rect1 = Rectangle { width: 30, height: 50 };

println!("rect1's area is {}", rect1.area());

println!("rect1's perimeter {}", rect1.perimeter());

let  rect2 = Rectangle { width: 100, height: 40 };

println!("rect2 wider > rect2 wider: {}", rect2.wider(&rect1));

println!("rect2 heigher > rect1 heigher {}", rect2.heighter(&rect1));

let  rect3 = Rectangle::create(10, 20);

println!("rect3's is {:?}", rect3);

}

模块

我们使用mod关键字来表示一个模块,一个模块是否对外使用,这里什么引出了公有和私有性,比如说我们来定义一个模块


mod nation { // 定义模块 nation

pub  mod government { // 子模块 government(公开)

pub  fn  govern() {} // 公开函数

}

mod congress { // 子模块 congress(私有)

pub  fn  legislate() {} // 函数虽标记为 pub,但受模块可见性限制

}

mod court { // 子模块 court(私有)

fn  judicial() { // 私有函数

super::congress::legislate(); // 调用兄弟模块的函数

}

}

}

fn  main() {

nation::government::govern();

nation::congress::legislate();

}

或者你可以运行看看结果

继续看示例:


mod module_a {

pub  trait  Shape {

fn  play(&self) {

println!("1");

}

}

pub  struct  A;

impl  Shape  for  A {}

}

pub  mod module_b {

use  super::module_a::Shape;

use  super::module_a::A; // 这里只引入了另一个模块中的类型

pub  fn  doit() {

let  a = A;

a.play();

}

}

fn  main() {

module_b::doit();

}

总结

这里我们快速的过来一遍rust的基础语法,从基础数据类型,复合数据类型,流程控制和函数,看上去其实也不难的是吧,不过别担心,这里的内容只是rust的冰山一角。

本作品采用《CC 协议》,转载必须注明作者和本文链接
刻意学习
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
126
粉丝
107
喜欢
196
收藏
282
排名:333
访问:2.8 万
私信
所有博文
社区赞助商