目录
0 概述
1 环境搭建
2 简单认识Rust
3 Rust语言
3.1 基本构成
3.2 语句与表达式
3.3 变量
3.4 函数
3.5 流程控制
3.6 基本数据类型
3.7 复合数据类型
3.8 注释与打印
0 概述
Rust是一门同时追求
安全、并发和性能的
系统级编程语言,有直接操作底层硬件的能力,同时拥有高级的抽象表达能力。
- Rust语言注重安全。它建立了严格的安全内存管理模型:1.
所有权系统;2.
借用和生命周期
- Rust语言追求高效开发和性能。它的抽象是零成本的,并不会存在运行时性能开销,这一切都是在编译期完成的。要做到零成本抽象,Rust需要
泛型和
trait。
- Rust语言具有实用性。它已经为开发工业级产品做足准备;为了开发者更方便地相互协作,它提供了包管理器Cargo;为了方便开发者学习,它在不断地完善官方文档,不断改进Rust,提升Rust的友好度。
- Rust本身就是一个开源项目。
- Rust应用在多个领域中,如数据库、游戏、云计算、安全、区块链等。
1 环境搭建
请参考这里的链接—->>>>>时光隧道
2 简单认识Rust
参考官方的入门教程,简单了解Rust。
3 Rust语言
3.1 基本构成
- 语言规范
《参考手册》是官方团队维护的一份参考文档,虽然并非正式的语言规范,但它比“圣经”更加详尽全面。
RFC文档,适合初学者深入了解某个语言特性的来龙去脉。
- 编译器rustc
rustc可以运行在多个平台上(windows、Mac、linux),支持交叉编译,支持多目标平台。
- 核心库
核心库不依赖于操作系统和网络等相关的库,不提供并发和IO。可以通过在模块顶部引入#![no_std]来使用核心库。做嵌入式开发时,核心库是必需的。
- 标准库
标准库提供应用程序开发所需要的基础和跨平台支持。如并发、IO和运行时,还有与OS交互的基础功能,错误处理以及各种迭代器。
- 包管理器 Cargo
Cargo类似于Python中的pip、Node.js中的npm,它还能够管理整个工作流程,从创建项目、运行单元测试和基准测试,到构建发布链接库,再到运行可执行文件。
3.2 语句与表达式
Rust语法中可以分成两大类:语句和表达式。
- 语句有可分为两种:(1)声明语句,用于声明变量、常量、结构体等;(2)表达式语句,特指以分号结尾的表达式。
- 表达式,主要用于计算求值。
如下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 1fn main() {
2 let a = 3;
3 let b = 4;
4 println!("sum_one() = {}",sum_one(a,b));
5 println!("sum_two() = {}",sum_two(a,b));
6 println!("sum_three() = {:?}",sum_three(a,b));
7}
8
9fn sum_one(a:i32,b:i32)->i32{
10 //这是表达式,sum函数直接返回表达式求得的值
11 a + b
12}
13
14fn sum_two(a:i32,b:i32)->i32{
15 //这是表达式语句
16 let c = a + b;
17 //这是表达式,返回c的值
18 c
19}
20
21fn sum_three(a:i32,b:i32)->(){
22 //这是表达式语句,分号后面什么都没有时,返回单元值()
23 a + b;
24}
25
运行结果:
Rust中的表达式分为位置表达式和值表达式。
- 位置表达式,表示内存位置。
- 值表达式,一般只引用了某个存储单元地址中的数据。
3.3 变量
变量的创建
1
2
3
4
5
6
7
8
9 1fn main() {
2 //关键字let用于创建变量
3 //创建变量a,默认为不可变
4 let a = 3;
5 //给变量a重新赋值,会产生编译时错误
6 a = 4;
7 println!("a = {}",a);
8}
9
错误信息如下:
如果想为变量二次赋值,修改如下:
1
2
3
4
5
6
7
8 1fn main() {
2 //关键字mut,用于声明变量为可变的
3 let mut a = 3;
4 println!("a = {}",a);
5 a = 4;
6 println!("a = {}",a);
7}
8
3.4 函数
定义
1
2
3
4
5
6
7
8
9
10
11
12 1//关键字fn,用于定义函数
2fn main() {//程序的入口
3 let a = sum(1,2);
4 println!("a = {}",a);
5}
6//参数:(a:i32,b:i32),i32为参数的数据类型
7//返回值:-> i32,i32为返回值的数据类型
8fn sum(a:i32 ,b:i32) -> i32{
9 //不使用关键字return
10 a + b
11}
12
作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1fn main(){
2 let v = "hello world!";
3 assert_eq!(v,"hello world!");//使用断言验证字符串
4 let v = "hello Rust!";//以上两个变量v的作用域为main函数内
5 //这种连续用let定义同名变量的做法叫做“变量遮蔽”
6 assert_eq!(v,"hello Rust!");
7 {
8 let v = "hello world!";//这个变量v的作用域为当前大括号内
9 assert_eq!(v,"hello world!");
10 }
11 assert_eq!(v,"hello Rust!");
12 //这证明在词法作用域内使用花括号开辟新的词法作用域后,两个作用域是互相独立的
13}
14
3.5 流程控制
条件表达式
1
2
3
4
5
6
7
8
9
10
11 1fn main(){
2 let n =5;
3 let big_n = if n < 10 && n > -10{
4 n * 10
5 }else{
6 "hello"
7 };
8 //if和else返回的类型不同,编译时会报错
9 println!("big_n = {}",big_n);
10}
11
循环表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 1fn main(){
2 //for...in循环表达式
3 for n in 1..101{//实质是一个迭代器,n的取值范围为1到100
4 if n % 15 == 0{
5 println!("fizzbuzz");
6 }else if n % 3 == 0{
7 println!("fizz");
8 }else if n % 5 == 0{
9 println!("buzz");
10 }else{
11 println!("{}",n);
12 }
13 }
14}
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1fn main(){
2 //while true
3 let y = while_true(5);
4 assert_eq!(y,6);
5}
6
7fn while_true(x:i32) -> i32 {
8 //编译时会报错,由于返回期望返回的值类型为i32
9 //但编译时编译器会忽略循环体内的表达式
10 //所以编译器认为返回值为单元值()
11 while true{
12 return x + 1;
13 }
14 //可以这样修改
15 //x
16 //程序不会运行到这里,只是告诉编译器函数返回的是i32类型的数据
17}
18//编译器建议使用loop代替while true
19
1
2
3
4
5
6
7
8
9
10
11 1fn main(){
2 let mut n = 10;
3 loop{
4 n = n - 1;
5 if n < 0{
6 break;
7 }
8 println!("n = {}",n);
9 }
10}
11
match表达式与模式匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1fn main(){
2 let number = 8;
3 //match模式匹配,类似于其他语言中的switch...case
4 match number{
5 //match分支(左边 => 右边),左边就是模式,右边就是执行代码
6 //与if表达式类似,所有分支都必须返回同一个类型
7 0 => println!("Origin"),//单值匹配
8 1...3 => println!("All"),//范围匹配
9 |5|7|13 => println!("Bad Luck"),//多值匹配
10 n @ 42 => println!("Answer is {}",n),//将42绑定到变量n,供执行语句使用
11 _ => println!("Common"),//类似于default,处理剩余情况
12 }
13}
14
if let 和 whlie let 表达式
1
2
3
4
5
6
7
8
9
10 1fn main(){
2 let boolean = true;
3 let mut binary = 0;
4 //与match类似,if let表达式等号左边为模式,右边为要匹配的值
5 if let true = boolean{//如果boolean为true,将binary修改为1
6 binary = 1;
7 }
8 println!("binary = {}",binary);
9}
10
1
2
3
4
5
6
7
8
9 1fn main(){
2 let mut v = vec![1,2,3,4,5];//动态数组
3 while let Some(x) = v.pop() {
4 //Some()用于匹配数组中的元素,v.pop()返回的Option类型会自动绑定到变量x
5 println!("{}",x);
6 }
7 //当数组的值取空时,自动跳出循环
8}
9
3.6 基本数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 1fn main(){
2 //布尔类型
3 let x = true;//等价于下面的y声明语句,Rust会自动推断其类型为bool
4 let y : bool = false;//:bool用于显式声明数据类型
5
6 //基本数字类型
7 //固定大小的类型
8 //无符号整数
9 //u8,大小1个字节,范围0 ~ 2^(8-1)
10 //u16,大小2个字节,范围0 ~ 2^(16-1)
11 //u32,大小4个字节,范围0 ~ 2^(32-1)
12 //u64,大小8个字节,范围0 ~ 2^(64-1)
13 //u128,大小16个字节,范围0 ~ 2^(128-1)
14 //有符号整数
15 //i8,大小1个字节,范围-2^7 ~ 2^(7-1)
16 //i16,大小2个字节,范围-2^15 ~ 2^(15-1)
17 //i32,大小4个字节,范围-2^31 ~ 2^(31-1)
18 //i64,大小8个字节,范围-2^63 ~ 2^(63-1)
19 //128,大小16个字节,范围-2^127 ~ 2^(127-1)
20 let num = 42u32;//类型后缀,代表这是一个u32类型,无符号32位整数类型
21 let num : u32 = 42;//与上面的声明效果相同
22 let num = 42;//不加后缀或指定数据类型,Rust默认推断为i32类型
23 let num = 0x2A;//前缀0x表示十六进制
24 let num = 0o106;//前缀0o表示八进制
25 let num = 0b1101_1011;//前缀0b表示二进制
26 assert_eq!(b'*',42u8);//字节字面量,等价于42u8
27 //动态大小类型
28 //usize,大小4个或8个字节,范围0~2^(32-1)或0~2^(64-1),取决于机器字长
29 //isize,大小4个或8个字节,范围-2^31~2^(31-1)或-2^63~2^(63-1),取决于机器字长
30 //浮点数类型
31 //f32,大小4个字节,至少6位有效数字,范围-3.4*10^38~3.4*10^38
32 //f64,大小8个字节,至少15位有效数字,范围-1.8*10^308~1.8*10^308
33 let num = 3.1415926f64;//声明64位浮点数
34 println!("{:?}",std::f32::INFINITY);//无穷大
35 println!("{:?}",std::f32::NEG_INFINITY);//负无穷大
36 println!("{:?}",std::f32::NAN);//非数字值
37 println!("{:?}",std::f32::MIN);//最小值
38 println!("{:?}",std::f32::MAX);//最大值
39
40 //字符类型
41 //使用单引号定义字符类型,大小4个字节
42 let ch = 'r';
43 println!("size_of_val(ch) = {}",std::mem::size_of_val(&ch));//打印ch的大小,单位字节
44}
45
数组类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 1fn main(){
2 //定义数据类型为u32,固定长度为5,名叫arr的不可变绑定数组
3 let arr:[u32;5] = [1,2,3,4,5];
4 //即使定义为let mut数组,也只能修改已存在索引位上的元素
5 //let mut arr:[u32;5] = [1,2,3,4,5];
6 //arr[5] = 6;//编译时报错
7 //arr[2] = 30;
8
9 //创建初始值为0且指定长度为5的数组
10 //let arr = [0;5];
11
12 //范围类型包括左闭右开和全闭两种区间
13
14 //左闭右开区间示例,0..5表示左闭右开区间,即0~4
15 for index in 0..5{
16 println!("{}",arr[index]);
17 }
18 println!("\r\n------------------------\r\n");
19 //全闭区间示例,1..=3表示全闭区间,即1~3
20 for index in 1..=3{
21 println!("{}",arr[index]);
22 }
23
24 //越界访问时,编译器会报错
25 //println!("{}",arr[5]);
26}
27
切片类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 1fn main(){
2 let arr:[i32;5] = [1,2,3,4,5];
3
4 //使用&操作对数组进行引用
5 let a = &arr;
6 println!("{:?}",a);
7 println!("{}",a[1]);
8
9 println!("--------------------------");
10
11 //也可以结合范围对数组进行切割
12 let a = &arr[1..];//获取arr数组中索引位置1之后的所有元素
13 println!("{:?}",a);//打印切片
14 println!("{}",a[0]);//打印切片中索引位置0的元素
15
16 println!("--------------------------");
17
18 println!("{}",a.len());//获取切片长度
19 println!("{}",a.is_empty());//判断数组是否为空
20
21 println!("--------------------------");
22
23 //定义可变绑定数组
24 let mut arr_1:[i32;5] = [1,2,3,4,5];
25 //定义可变切片
26 let a_1 = &mut arr_1[1..=2];
27 println!("{:?}",a_1);
28 //可以通过索引位置修改切片
29 a_1[0] = 20;
30 println!("{:?}",a_1);
31}
32
str字符串类型
Rust将字符串分成两类:
-
固定长度的字符串,不可随便更改其长度,即str字符串
-
可增长字符串,可以随意改变其长度,即String字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 1fn main(){
2 let s:&'static str = "Hello World!";//静态生命周期字符串
3
4 let ptr = s.as_ptr();//字符串指针
5 let len = s.len();//字符串长度
6
7 println!("len = {}",len);
8
9 let s_1 = unsafe{
10 //通过传入指针和长度,将相应的字节序列转换为切片类型&[u8]
11 let slice = std::slice::from_raw_parts(ptr,len);
12 //将得到的切片类型转换为str字符串
13 std::str::from_utf8(slice)
14 };//因为整个过程都没有验证字节序列是否为合法的UTF8字符串,所以放在unsafe块内
15
16 println!("s_1 = {:?}",s_1);
17}
18
原生指针
Rust提供多种类型的指针:
- 引用
- 原生指针
- 函数指针
- 智能指针
原生指针主要用于unsafe块中,直接使用原生指针是不安全的。
Rust支持两种原生指针:
-
不可变原生指针 *const T
-
可变原生指针 *mut T
1
2
3
4
5
6
7
8
9
10
11
12 1fn main(){
2 let mut x = 10;
3 let ptr_x = &mut x as *mut i32;//将可变引用转换为可变原生指针ptr_x
4 let y = Box::new(20);//在堆内存中存储数字20
5 let ptr_y = &*y as *const i32;//转换为不可变原生指针ptr_y
6 unsafe{//操作原生指针要使用unsafe块
7 *ptr_x += *ptr_y;//对ptr_x、ptr_y指针解引用,求和
8 }
9
10 println!("x = {}",x);
11}
12
never类型
即!(感叹号)。该类型用于表示永远不可能有返回值的计算类型。never类型可以强制转换成其他任何类型。
3.7 复合数据类型
Rust提供的复合数据类型有:
元组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 1fn main(){
2 //定义元组
3 let tuple:(&'static str,i32,char) = ("hello",-1,'s');
4 println!("{:?}",tuple);//打印元组
5 println!("tuple.1 = {}",tuple.1);
6 println!("-------------------------");
7 //利用元组让函数返回多个值
8 //let支持模式匹配,所以可以用来解构元组
9 let (x,y) = foo((2,3));//函数返回一个元组,通过let解构,元组第一位绑定到x,第二位绑定到y
10 println!("x = {}",x);//可以直接使用x、y
11 println!("y = {}",y);
12 println!("-------------------------");
13 //当元组只有一个值时,需要加逗号,即(0,)
14 //前面讲到的单元类型就是一个空元组,即()
15}
16
17fn foo(x:(i32,i32)) -> (i32,i32){
18 (x.0 + 1,x.1 + 1)
19}
20
结构体,分三种:
-
具名结构体
1
2
3
4
5
6
7
8 1//结构体里面字段格式为name:type
2//name是字段名称
3//type是字段的类型
4struct People{
5 name:&'static str,
6 gender:u32,
7}
8
-
元组结构体
1
2
3
4
5
6
7
8
9 1//元组结构体后面要加分号
2//访问元组结构体中的字段,使用圆点记号(.)
3struct Color(i32,i32,i32);
4
5fn main(){
6 let color = Color(0,1,2);
7 println!("{}",color.0);//类似于元组的使用方法
8}
9
-
单元结构体
1
2
3 1//没有任何字段的结构体,称为单元结构体
2struct Empty;
3
枚举体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 1//定义枚举体的关键字为Enum
2//无参数枚举体
3enum Number {
4 Zero,
5 One,
6 Two,
7}
8//使用方法
9let a = Number::One;
10
11//类C枚举体
12enum Color{
13 Red = 0xff0000,
14 Green = 0x00ff00,
15 Blue = 0x0000ff,
16}
17
18
19//带参数枚举体
20enum IpAddr{
21 V4(u8,u8,u8,u8),
22 V6(String),
23}
24
3.8 注释与打印
普通注释
//对整行注释
/*……*/对区块注释
文档注释
///,可以生成库文档,一般用于函数或结构体的说明,置于说明对象上方
//!,也可以生成库文档,一般用于说明整个模块的功能,置于模块文件的头部
格式化打印
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 1fn main(){
2 let a = 6;
3 //nothing代表Display
4 println!("{}",1);
5 //?代表Debug
6 println!("{:?}",2);
7 //o代表八进制
8 println!("{:o}",3);
9 //x代表十六进制小写
10 println!("{:x}",4);
11 //X代表十六进制大写
12 println!("{:X}",5);
13 //p代表指针
14 println!("{:p}",&a);
15 //b代表二进制
16 println!("{:b}",7);
17 //e代表指数小写
18 println!("{:e}",3.1415926);
19 //E代表指数大写
20 println!("{:E}",3.1415926);
21}
22