0°

Rust—入门

目录

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将字符串分成两类:

  1. 固定长度的字符串,不可随便更改其长度,即str字符串

  2. 可增长字符串,可以随意改变其长度,即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提供多种类型的指针:

  1. 引用
  2. 原生指针
  3. 函数指针
  4. 智能指针

原生指针主要用于unsafe块中,直接使用原生指针是不安全的。

Rust支持两种原生指针:

  1. 不可变原生指针 *const T

  2. 可变原生指针 *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

 

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!