目录
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)表达式语句,特指以分号结尾的表达式。
- 表达式,主要用于计算求值。
如下面的例子:
fn main() {
let a = 3;
let b = 4;
println!("sum_one() = {}",sum_one(a,b));
println!("sum_two() = {}",sum_two(a,b));
println!("sum_three() = {:?}",sum_three(a,b));
}
fn sum_one(a:i32,b:i32)->i32{
//这是表达式,sum函数直接返回表达式求得的值
a + b
}
fn sum_two(a:i32,b:i32)->i32{
//这是表达式语句
let c = a + b;
//这是表达式,返回c的值
c
}
fn sum_three(a:i32,b:i32)->(){
//这是表达式语句,分号后面什么都没有时,返回单元值()
a + b;
}
运行结果:
Rust中的表达式分为位置表达式和值表达式。
- 位置表达式,表示内存位置。
- 值表达式,一般只引用了某个存储单元地址中的数据。
3.3 变量
变量的创建
fn main() {
//关键字let用于创建变量
//创建变量a,默认为不可变
let a = 3;
//给变量a重新赋值,会产生编译时错误
a = 4;
println!("a = {}",a);
}
错误信息如下:
如果想为变量二次赋值,修改如下:
fn main() {
//关键字mut,用于声明变量为可变的
let mut a = 3;
println!("a = {}",a);
a = 4;
println!("a = {}",a);
}
3.4 函数
定义
//关键字fn,用于定义函数
fn main() {//程序的入口
let a = sum(1,2);
println!("a = {}",a);
}
//参数:(a:i32,b:i32),i32为参数的数据类型
//返回值:-> i32,i32为返回值的数据类型
fn sum(a:i32 ,b:i32) -> i32{
//不使用关键字return
a + b
}
作用域
fn main(){
let v = "hello world!";
assert_eq!(v,"hello world!");//使用断言验证字符串
let v = "hello Rust!";//以上两个变量v的作用域为main函数内
//这种连续用let定义同名变量的做法叫做“变量遮蔽”
assert_eq!(v,"hello Rust!");
{
let v = "hello world!";//这个变量v的作用域为当前大括号内
assert_eq!(v,"hello world!");
}
assert_eq!(v,"hello Rust!");
//这证明在词法作用域内使用花括号开辟新的词法作用域后,两个作用域是互相独立的
}
3.5 流程控制
条件表达式
fn main(){
let n =5;
let big_n = if n < 10 && n > -10{
n * 10
}else{
"hello"
};
//if和else返回的类型不同,编译时会报错
println!("big_n = {}",big_n);
}
循环表达式
fn main(){
//for...in循环表达式
for n in 1..101{//实质是一个迭代器,n的取值范围为1到100
if n % 15 == 0{
println!("fizzbuzz");
}else if n % 3 == 0{
println!("fizz");
}else if n % 5 == 0{
println!("buzz");
}else{
println!("{}",n);
}
}
}
fn main(){
//while true
let y = while_true(5);
assert_eq!(y,6);
}
fn while_true(x:i32) -> i32 {
//编译时会报错,由于返回期望返回的值类型为i32
//但编译时编译器会忽略循环体内的表达式
//所以编译器认为返回值为单元值()
while true{
return x + 1;
}
//可以这样修改
//x
//程序不会运行到这里,只是告诉编译器函数返回的是i32类型的数据
}
//编译器建议使用loop代替while true
fn main(){
let mut n = 10;
loop{
n = n - 1;
if n < 0{
break;
}
println!("n = {}",n);
}
}
match表达式与模式匹配
fn main(){
let number = 8;
//match模式匹配,类似于其他语言中的switch...case
match number{
//match分支(左边 => 右边),左边就是模式,右边就是执行代码
//与if表达式类似,所有分支都必须返回同一个类型
0 => println!("Origin"),//单值匹配
1...3 => println!("All"),//范围匹配
|5|7|13 => println!("Bad Luck"),//多值匹配
n @ 42 => println!("Answer is {}",n),//将42绑定到变量n,供执行语句使用
_ => println!("Common"),//类似于default,处理剩余情况
}
}
if let 和 whlie let 表达式
fn main(){
let boolean = true;
let mut binary = 0;
//与match类似,if let表达式等号左边为模式,右边为要匹配的值
if let true = boolean{//如果boolean为true,将binary修改为1
binary = 1;
}
println!("binary = {}",binary);
}
fn main(){
let mut v = vec![1,2,3,4,5];//动态数组
while let Some(x) = v.pop() {
//Some()用于匹配数组中的元素,v.pop()返回的Option类型会自动绑定到变量x
println!("{}",x);
}
//当数组的值取空时,自动跳出循环
}
3.6 基本数据类型
fn main(){
//布尔类型
let x = true;//等价于下面的y声明语句,Rust会自动推断其类型为bool
let y : bool = false;//:bool用于显式声明数据类型
//基本数字类型
//固定大小的类型
//无符号整数
//u8,大小1个字节,范围0 ~ 2^(8-1)
//u16,大小2个字节,范围0 ~ 2^(16-1)
//u32,大小4个字节,范围0 ~ 2^(32-1)
//u64,大小8个字节,范围0 ~ 2^(64-1)
//u128,大小16个字节,范围0 ~ 2^(128-1)
//有符号整数
//i8,大小1个字节,范围-2^7 ~ 2^(7-1)
//i16,大小2个字节,范围-2^15 ~ 2^(15-1)
//i32,大小4个字节,范围-2^31 ~ 2^(31-1)
//i64,大小8个字节,范围-2^63 ~ 2^(63-1)
//128,大小16个字节,范围-2^127 ~ 2^(127-1)
let num = 42u32;//类型后缀,代表这是一个u32类型,无符号32位整数类型
let num : u32 = 42;//与上面的声明效果相同
let num = 42;//不加后缀或指定数据类型,Rust默认推断为i32类型
let num = 0x2A;//前缀0x表示十六进制
let num = 0o106;//前缀0o表示八进制
let num = 0b1101_1011;//前缀0b表示二进制
assert_eq!(b'*',42u8);//字节字面量,等价于42u8
//动态大小类型
//usize,大小4个或8个字节,范围0~2^(32-1)或0~2^(64-1),取决于机器字长
//isize,大小4个或8个字节,范围-2^31~2^(31-1)或-2^63~2^(63-1),取决于机器字长
//浮点数类型
//f32,大小4个字节,至少6位有效数字,范围-3.4*10^38~3.4*10^38
//f64,大小8个字节,至少15位有效数字,范围-1.8*10^308~1.8*10^308
let num = 3.1415926f64;//声明64位浮点数
println!("{:?}",std::f32::INFINITY);//无穷大
println!("{:?}",std::f32::NEG_INFINITY);//负无穷大
println!("{:?}",std::f32::NAN);//非数字值
println!("{:?}",std::f32::MIN);//最小值
println!("{:?}",std::f32::MAX);//最大值
//字符类型
//使用单引号定义字符类型,大小4个字节
let ch = 'r';
println!("size_of_val(ch) = {}",std::mem::size_of_val(&ch));//打印ch的大小,单位字节
}
数组类型
fn main(){
//定义数据类型为u32,固定长度为5,名叫arr的不可变绑定数组
let arr:[u32;5] = [1,2,3,4,5];
//即使定义为let mut数组,也只能修改已存在索引位上的元素
//let mut arr:[u32;5] = [1,2,3,4,5];
//arr[5] = 6;//编译时报错
//arr[2] = 30;
//创建初始值为0且指定长度为5的数组
//let arr = [0;5];
//范围类型包括左闭右开和全闭两种区间
//左闭右开区间示例,0..5表示左闭右开区间,即0~4
for index in 0..5{
println!("{}",arr[index]);
}
println!("\r\n------------------------\r\n");
//全闭区间示例,1..=3表示全闭区间,即1~3
for index in 1..=3{
println!("{}",arr[index]);
}
//越界访问时,编译器会报错
//println!("{}",arr[5]);
}
切片类型
fn main(){
let arr:[i32;5] = [1,2,3,4,5];
//使用&操作对数组进行引用
let a = &arr;
println!("{:?}",a);
println!("{}",a[1]);
println!("--------------------------");
//也可以结合范围对数组进行切割
let a = &arr[1..];//获取arr数组中索引位置1之后的所有元素
println!("{:?}",a);//打印切片
println!("{}",a[0]);//打印切片中索引位置0的元素
println!("--------------------------");
println!("{}",a.len());//获取切片长度
println!("{}",a.is_empty());//判断数组是否为空
println!("--------------------------");
//定义可变绑定数组
let mut arr_1:[i32;5] = [1,2,3,4,5];
//定义可变切片
let a_1 = &mut arr_1[1..=2];
println!("{:?}",a_1);
//可以通过索引位置修改切片
a_1[0] = 20;
println!("{:?}",a_1);
}
str字符串类型
Rust将字符串分成两类:
- 固定长度的字符串,不可随便更改其长度,即str字符串
- 可增长字符串,可以随意改变其长度,即String字符串
fn main(){
let s:&'static str = "Hello World!";//静态生命周期字符串
let ptr = s.as_ptr();//字符串指针
let len = s.len();//字符串长度
println!("len = {}",len);
let s_1 = unsafe{
//通过传入指针和长度,将相应的字节序列转换为切片类型&[u8]
let slice = std::slice::from_raw_parts(ptr,len);
//将得到的切片类型转换为str字符串
std::str::from_utf8(slice)
};//因为整个过程都没有验证字节序列是否为合法的UTF8字符串,所以放在unsafe块内
println!("s_1 = {:?}",s_1);
}
原生指针
Rust提供多种类型的指针:
- 引用
- 原生指针
- 函数指针
- 智能指针
原生指针主要用于unsafe块中,直接使用原生指针是不安全的。
Rust支持两种原生指针:
- 不可变原生指针 *const T
- 可变原生指针 *mut T
fn main(){
let mut x = 10;
let ptr_x = &mut x as *mut i32;//将可变引用转换为可变原生指针ptr_x
let y = Box::new(20);//在堆内存中存储数字20
let ptr_y = &*y as *const i32;//转换为不可变原生指针ptr_y
unsafe{//操作原生指针要使用unsafe块
*ptr_x += *ptr_y;//对ptr_x、ptr_y指针解引用,求和
}
println!("x = {}",x);
}
never类型
即!(感叹号)。该类型用于表示永远不可能有返回值的计算类型。never类型可以强制转换成其他任何类型。
3.7 复合数据类型
Rust提供的复合数据类型有:
元组
fn main(){
//定义元组
let tuple:(&'static str,i32,char) = ("hello",-1,'s');
println!("{:?}",tuple);//打印元组
println!("tuple.1 = {}",tuple.1);
println!("-------------------------");
//利用元组让函数返回多个值
//let支持模式匹配,所以可以用来解构元组
let (x,y) = foo((2,3));//函数返回一个元组,通过let解构,元组第一位绑定到x,第二位绑定到y
println!("x = {}",x);//可以直接使用x、y
println!("y = {}",y);
println!("-------------------------");
//当元组只有一个值时,需要加逗号,即(0,)
//前面讲到的单元类型就是一个空元组,即()
}
fn foo(x:(i32,i32)) -> (i32,i32){
(x.0 + 1,x.1 + 1)
}
结构体,分三种:
- 具名结构体
//结构体里面字段格式为name:type
//name是字段名称
//type是字段的类型
struct People{
name:&'static str,
gender:u32,
}
- 元组结构体
//元组结构体后面要加分号
//访问元组结构体中的字段,使用圆点记号(.)
struct Color(i32,i32,i32);
fn main(){
let color = Color(0,1,2);
println!("{}",color.0);//类似于元组的使用方法
}
- 单元结构体
//没有任何字段的结构体,称为单元结构体
struct Empty;
枚举体
//定义枚举体的关键字为Enum
//无参数枚举体
enum Number {
Zero,
One,
Two,
}
//使用方法
let a = Number::One;
//类C枚举体
enum Color{
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff,
}
//带参数枚举体
enum IpAddr{
V4(u8,u8,u8,u8),
V6(String),
}
3.8 注释与打印
普通注释
//对整行注释
/*......*/对区块注释
文档注释
///,可以生成库文档,一般用于函数或结构体的说明,置于说明对象上方
//!,也可以生成库文档,一般用于说明整个模块的功能,置于模块文件的头部
格式化打印
fn main(){
let a = 6;
//nothing代表Display
println!("{}",1);
//?代表Debug
println!("{:?}",2);
//o代表八进制
println!("{:o}",3);
//x代表十六进制小写
println!("{:x}",4);
//X代表十六进制大写
println!("{:X}",5);
//p代表指针
println!("{:p}",&a);
//b代表二进制
println!("{:b}",7);
//e代表指数小写
println!("{:e}",3.1415926);
//E代表指数大写
println!("{:E}",3.1415926);
}