Rust基础
无代码,只作知识点记住,详细可查官方wiki:doc
包管理器
包管理器的详细操作这里就不写了,网上都可以找到。
使用cargo
时,可以安装一个插件: cargo-edit
。安装好后就可以使用cargo add [包名字]
来安装库,可以使用cargo rm [包名字]
来删除库。
变量与不可变性
变量的基础知识
- 可以使用
let
关键字来声明变量 - Rust支持类型推导,但可以显式指定变量的类型:
let x: i32 = 5;
- 变量名采用蛇形命名法,而枚举与结构体使用帕期卡命名法
- 如果一个变量没有使用到,可以使用前置下划线来消除警告
- 使用
as
实现强行类型转换:let a = 3.1; let b = a as i32;
- 基本数据类型的打印变量都已经实现了。
rust中的变量默认是不可变的
不可变性是rust实现其可靠性和安全性目标的关键。该方式可以让程序员了解程序状态的变化,并明确哪些部分的程序状态可能会发生变化。
如果要让一个变量是可变的,可以使用mut
关键字来实现。
1 | let mut y = 10; |
Shadowing Variables
Rust可以隐藏一个变量,因此可以声明一个与现有变量同名的新变量,该变量会隐藏之前的老变量。
1 | let x = 10; |
const 常量
- 常量的值必须在编译时已知的常量表达式,必须指定类型与值。
- 与C语言的宏定义不同,rust的const常量的值被直接嵌入到生成的pqnf机器代码中,而不是进行简单的字符替换。
- 常量名与静态变量命名必须全部大写,单词之间加入下划线
- 常量的作用域是块级作用域,它们只在声明它们的作用域内可见。
static静态变量
- 与const常量不同,static变量是在去年时分配内存的
- 其可以修改,可以使用unsafe修改,如果要打印,则也要在unsafe代码中打印
- 静态变量的生命周期为整个程序的运行时间
基本数据类型
- 带符号的整数(默认为i32)
- i8,i16,i32,i64,i128
- 不带符号的整数
- u8,u16,u32,u64,u128
- 由平台决定的
- usize,isize
- 浮点型(推荐i64)
- f32,f64
- 布尔型
- true, false
- 字符类型
- 支持unicode字符
- char类型要使用单引号
元组与数组
- 相同点:
- 元组与数组都是Compound Types(多个值组合在一起形成的类型),而Vec与Map都是Collection Types(可以包含多个值的动态大小的数据结构)
- 元组和数组长度都是固定的
- 均可以使用
mut
来设置为可变的。对于元组来说,修改前后的数据类型要保持一致。
- 不同点
- 元组:可以是不同的数据类型
- 数组:必须是相同的数据类型
数组
- 数组是固定长度的同构集合
- 创建方式
- [a,b,c]
- [value; size]
- 获取元素:arr[index]
- 获取长度:arr.len()
元组
- 元组是固定长度的异构集合
- Empty Tuple()
- 为函数的默认返回值
- 元组获取元素
- tup.index
- 没有len()
内存管理模型
- 纯给程序员(c++)
- 纯给gc(java)
- rust型
- 在编译时期做一系列的检查,如果检查到有问题,则不会通过
- 使用所有权机制来限制错误的产生
rust的内存管理模型
- 所有权系统(ownership system)
- 借用(borrowing)
- 不可变借用
- 可变引用
- 生命周期(lifetimes)
- 引入计数(reference counting)
表示字符串的两种类型
String
string是一个堆分配的可变字符串类型
- String是具有所有权的
- Struct中属性使用String
- 如果不使用显式声明生命周期无法使用&str
- 会带来很多隐患
&str
&str是指字符串切片的引用,是在栈上分配的
- 是不可变引用,指向存储在其他地方的UTF-8编码的字符串数据
- 由指针与长度构成
- 函数参数推荐使用&str(如果不想交出所有权)
- &str为参数,可以传递&str与&String
- &String为参数,只能传递&String不能传递&str
Enum枚举
- 枚举是一种用户自定义的数据类型,用于表示具有一组离散可能值的变量
- 每种可能值都称为”variant”
- 使用的方式为
枚举名::变体名
- 枚举的好处
- 可以使代码更严谨,更易读
- 更加健状
常用的枚举的类型:Option
与Result
匹配模式
枚举常与匹配模式一起用
- match关键字实现
- 必须覆盖所有的变体
- 可以用
_, ..=, 三元
等来进行匹配
结构体
结构体是一种用户定义的数据类型,用于创建自定义的数据结构。每条数据称为属性,通过(.)来访问结构体中的属性。
结构体中的方法(本质是一个关联函数)
这里的方法是指,通过实例调用(&self, &mut self, self)
结构体中的关联函数(类似于c++中的静态成员函数)
关联函数是与类型相关联的函数,调用时为结构体名::函数名
结构体中的关联变量(类型于c++中的静态成员变量)
这里的关联变量是指,和结构体类型相关的变量,也可以在特质或是枚举中调用。
Ownership所有权机制
所有权机制的规则
- 所有的值都只有一个拥有者
- 同一时刻,只能有一个所有者
- 当值超出了所用域以后,会自动的进行消除
rust中传值的方式
每当将值从一个位置传递到另一个位置时,borror checker都会重新评估所有权。
- 不可变借用:值的所有权仍归发送方所有,接收方直接接收对该值的引用,而不是该值的副本。倡,他们不能使用该引用来修改它指向的值,编译器不允许这样做。释放资源的责仍由发送方承担。仅当发件人本身超出满园时,才会删除该值。
- 使用可变的借用所有权和删除值的责任也由发送者承担。但是接收方能够通过他们接收的引用来修改该值。
- move:这是所有权从一个地点转移到另一个地点。borrow checker关于释放该值的决定将由该值的接收者通知。由于所有权已从发送方转移到接收方,因此发送方在将引用移动到另一个上下文后不能再使用该引用,发送方在移动后对value的任何使用都会导致错误。
以下是三种self的区别:
1 | &self 等于 (self: &Self) |
1 | &mut self等于(self:&mut Self) |
1 | self等于(self: Self) |
堆与栈
stack
- 堆栈将按照获取值的顺序存储值,并以相反的顺序删除值
- 操作高效,函数伤域就是在栈上
- 堆栈上存在的所有数据都必须具有已知的固定大小数据
在栈上的数据有:
- 基础类型
- tuple与array
- struct与枚举等,如果属性中有String等存储在堆上的数据类型时,会将其指向堆
heap
- 堆的规律性较差,当把一些东西放到请求的堆上时,会返回一个指针
- 长度不确定
在堆上的数据有:
- Box,Rc,String/Vec等
Box
Box是一个智能指针,它提供对堆分配内存的所有权。它允许将数据存储在堆上而不是栈上,并且在犁或移动时保持对数据的唯一拥有权。使用Box可以避免一些内存管理问题,如悬垂指针与重复释放
Box的主要的4个作用:
- 所有权转移
- 释放内存
- 解引用
- 构建递归数据结构
copy与clone
- move:所有权转移
- clone:深拷贝
- copy:在clone的基础上建立的标记特质(marker trait,类似于继承的关系)
特质(trait):
- 特质是一种定义共享行为的机制。clone也是特质。
- marker trait是一个没有任何方法的trait,它主要用于向编译器传递某些信息,以改变类型的默认行为。
一般来说在栈上的数据类型都默认copy,但struct 等默认为move,需要copy只需要设置数据类型实现copy特质即可,或是调用clone函数(需要实现clone特质)
流程控制与函数
流程控制
- 顺序结构
- 选择结构(if, switch)
- 循环结构(for,while, do-while)
- 跳转结构(break, continue, goto)
IF流程
- 执行流程可以被IF改变
- 可以嵌套使用
match表达式
- 用于模式匹配,允许更复杂的条件与分支
- 可以处理多个模式,提高代码的表达力
- 是表达式,可以返回值
两者对比
复杂性:if适用于简单的条件判断,而match更适用于复杂的模式匹配
表达力:match更灵活,可以处理多个条件与模式,使代码更清晰
返回值:两者都是表达式,可以返回值,但match通常用于更复杂的场景
循环
- loop循环:是一个无限循环
- while循环:与c语言一样
- for循环:与python类似
break 与 continue
break关键字用于立即编目循环,并跳出循环体
continue关键字用于立即跳过当前循环中剩余的代码,进入下一次循环
迭代
rust中的迭代主要通过迭代器实现, 迭代器是一个抽象,提供了一种访问集合元素的统一方式。
从实现上讲,在rust中,迭代器是一种实现了iterator trait的类型
循环与迭代的不同
循环适用于需要明确控制循环流程的情况,而迭代器则提供了一种更抽象的方式来处理集合元素。通常,推荐使用迭代器,因为它们可以提高代码的可读性和表达力。
for循环是一种语法结构,用于遍历集合中的元素,它依赖于集合类型实现Iterator trait.
在rust中,迭代器提供了一系列用于遍历集合元素的方法,比如next()
,map()
,filter()
等,可以让我们的代码更具有表达性。
函数
函数的定义:在rust中,可以使用fn关键字声明与定义函数,而main是程序的入口点的一种特殊的函数
参数与返回值:
- 函数可以接受零个或多个参数,每个参数都需要指定类型
- 函数可以有返回值,使用
->
指定返回值类型。如果函数没有返回值,可以使用->()
或省略这部分。
调用函数:调用函数时,使用函数名和传递给函数的实际参数。
copy by value
- 如果数据类型实现copy特质,则在函数传参时会实现copy by value操作。
- 会将实参拷贝为形参,形参的改变不会影响实参
- 如果要改变形参,需要添加mut
Struct ,枚举,集合等并没有实现copy trait,会实现move操作,会失去所有权
如果为数据类型实现了copy trait,则可以实现copy by value
函数值参数传递(move)
函数的代码本身通常是存储在可执行文件的代码段,而在调用时函数会在栈上开辟一个新的stack frame,用于存储函数的局部变量,参数和返回地址等信息,而当函数结束后会释放该空间。
而当传入non-copy value(Vec, String等)
- 传入函数时实参会转移value的所有权给形参,实参会失去value的所有权
- 而在函数结束时,value的所有权会释放
不可变借用
- 如果不想失去value的所有权,又没有修改value的需求,可以使用不可变借用
- 在rust中,可以将不可变引用作为函数的参数,从而在函数内部访问参数值但不能悠它。这有确保数据安全性,防止在多处同时对数据进行写操作,从而避免数据竞争。
- 如何使用不可变借用
- use * to deference,去获取以其的值
可变借用
如果有修改值的需求,可以使用可变借用,以允许在函数内部修改参数的值。这允许函数对参数进行写操作,但在同一时间内只能有一个可变引用。
需要在前加&mut
如何使用可变借用
- use * to deference,去获取以其的值
返回copy 与non-copy
以上两种类型的值都可以返回,而non-copy则是在堆上返回
一般来说,返回copy类型的值通常有更好的性能。因为copy类型的值是通过复制进行返回的,而不涉及堆上的内存的分配与释放,通常是在栈上分配。这样的操作比涉及在堆上的内存的分配和释放更为高效。
返回引用
在只有传入一个引用参数,只有一个返回引用时,生命周期不需要声明
其他情况下需要声明引用的生命周期
慎用'static'
高阶函数
高阶函数:rust使用高阶函数,即函数可以作为参数传递给其他函数,或者函数可以返回其他函数
高阶函数也是函数式编程的重要特性。
高阶函数与集合
- map函数:可以用于对一个集合中的每个元素应用一个函数,并返回包含结果的新集合。
- filter函数:用于过渡集合中的元素,根据一个谓词函数的返回值
- fold:该函数也可称为
reduce
,可以用于迭代集合的每个元素,并将它们累积到一个单一的结果中。
错误处理
rust中的错误
错误可以分为两种:
- recoverable error:有返回类型
- 返回Result类型
- 返回Option类型
- unrevocerable type:没有返回类型,直接崩溃
- panic macro将终止当前线程
Result
Result是一个枚举类型,有两个变体:Ok与Err。它通常用于表示函数的执行结果,其中Ok表示成功的结果,Err表示出现了错误
Option
Option也是一个枚举类型,有两个变体:some与None,它通常用于表示一个可能为穿的值。
panic!
当程序遇到无法继续执行的错误时,可以使用panic!
宏来引发恐慌。恐慌会导致程序立即终止,并显示一条错误消息。
错误处理
unwrap()
该方法并不安全
unwrap()是Result与Option类型提供的方法之一。它是一个简便的方法,用于获取Ok或Some的值,如果是Err或None则会引发panic
?运算符
?用于简化Result或Option类型的错误传播。它只能用于返回Result或Option的函数中,并且在函数内部可以像使用unwarp()一样访问Ok或Some的值,但是如果是Err或None则会提前返回。
自定义Error
自定义的Error的三个步骤
- 定义错误类型结构体:创建一个结构体来表示你的错误类型,通常包含一些字段来描述错误的详细信息
- 实现std::fmt::Display trait,实现这个trait以定义如何展示错误信息。这是为了使错误可以以人类可读的方式打印出来。
- 实现std::error::Error trait:实现这个trait以满足Rust的错误处理机制的要求。