Rust基础
阅读原文时间:2023年07月09日阅读:3

Rust的编译器叫rustc,类似javac一样,负责将源代码编译成可执行文件或者库文件(.a/.so/.lib/.dll等)

Rust语言由核心库和标准库组成,核心库是标准库的基础,核心库不依赖于操作系统和网络相关的库,一般写嵌入式应用会用到;

可以在模块顶部引入#![no_std]来使用核心库;

标准库则一般用来开发应用程序,它提供了开发应用程序所需的基础和跨平台支持,比如println!宏其实就是标准库里的,它依赖操作系统提供的API来实现在标准输出流里打印数据;

Rust的包管理器是cargo【马车】,而Rust里每个项目编译成的库叫做crate,类似Java里的jar包;

cargo除了是包管理器,它还能用于创建可执行程序项目和库项目,命令分别是:

1.cargo new exec_proj

2.cargo new --lib lib_proj

然后可以通过cargo build和cargo run对项目进行编译和允许;

Rust里语法可以分为两大类,即语句和表达式;

语句是指分号结尾,或者花括号结尾的代码,比如输出语句,变量声明语句或结构体声明语句等等;

表达式则是指没有分号的表达式,如果它处于某个X块或函数最后一行,那么它将自动作为X块或函数的返回值,比如1 + 1就是一个表达式,放在函数最后表示这里将return 2;

变量默认是不可变的,即let a = 3;a = 4;会报错;但是可以通过let mut a = 3;来将a声明为可变的变量;将3赋值给a也叫绑定;a本质上是一个是i32类型指向了3所在空间的地址的一个指针数值;

虽然变量默认是不可变的,但是变量是可以转移/移动所有权的,即let a = M{..};let b = a;此时将a对M{..}对象的所有权转移给了b;【注意,基础类型是复制语义不会移动所有权】

其实Rust里变量名,引用,原生指针和智能指针本质上都是指向一块地址空间的起始地址【但是这个指针在Rust编译器里还拥有对应地址空间的大小,空间的结构等数据,但是它编译后则会自动分解成对不同位置进行操作的汇编代码】;

原生指针可以通过let a = 3;let ptr = &mut a as *mut i32;来获取原生指针【原生指针分为可变和不可变原生指针,这里*mut i32就是可变,*const i32就是不可变,可变表示可以对其指向的空间的内容修改,通过*ptr = 8;来实现,即原生指针的解引用(解指针)也是用*】

变量也可以通过变量遮蔽实现同一个作用域定义同名变量,比如let a = 3; let a = "kkk".to_string();

它的作用一般是比如let a = Some(44);let a = a.unwrap();

函数和方法的区别是全局的函数一般就叫函数,而在impl块属于某个结构体的函数叫做方法,比如AClass的test方法(还可以分静态方法或实例方法,实例方法第一个参数是&self或&mut self);

函数和方法的声明都是fn关键字进行声明;

闭包则本质上是一个实现了Fn之类的trait的匿名结构体;

函数和闭包都能作为函数参数或返回值(函数指针fn(i32, i32) -> i32),不同的是,函数无法捕获上下文变量;

Rust有一种const函数,即这个函数可以当做常量来使用,它的值是在编译期间确定的,这个是Rust实现反射的基础,写法为const fn lala() -> i32 {..}

函数可以在函数内部声明;

条件表达式:和其他语言不同,Rust的if表达式可以返回值,但是注意不同分支返回的值的类型必须一样,如let a = if (…) { 3} else {4}

循环表达式:可以用for i in 0..10 {..}实现类似C语言里for(int i = 0;i<10;i++){..},其中in的参数可以是范围类型(不过0..10应该也是范围类型的简写,少了括号);

还有loop {…}循环【恒真】和while boolean{..}循环;

match表达式:它有点像switch,但是比switch灵活很多,写法为:match variable {0 => ….,1…3 => …..,5 | 7 => …..,_=>…..,},这里对variable变量进行匹配,如果值是0则,是1到3则,是5或7则,其他的则,但是要注意每个分支返回的类型必须相同;

数值类型有:u8-u128表示一字节无符号值到16个字节无符号值,对应的还有i8-i128表示有符号;其中还有usize表示是跟机器字长相关的无符号整数;isize则是有符号;

浮点型的则有f32和f64;

字符类型为:char,它占4个字节(UTF-8);

数组类型:[1,2,3]这样的则是数组对象,它的类型定义可以是[i32; 3]前面表示类型,后面表示长度,还能用[1;3]表示长度是3的i32类型(默认是i32)且数组里每个元素初始值为1;

范围类型:用(1..5)表示,注意这种写法是左闭右开,即1,2,3,4,这个和数学上的写法不同;如果是全闭则是(1..=5);

切片类型:貌似就是数组或向量或字符串的引用?

字符串类型:str,它类似Java里的String,是一个字符串常量,长度内容不可变;而Rust里的String则是长度可变的字符串;

never类型:它的类型名叫!,表示一定不会赋值的类型,它感觉上和单元类型很像(单元类型是没什么意义,类似void),比如panic!宏的返回值就是!类型,由于产生了panic所以一定不会返回值,因此这种概念里的返回值类型就是never类型!

元组类型:元组类型是异构且可以解构的类型,用let tuple: (i32, &str) = (3, "bb");这种写法,解构元组则是let (x, y) = tuple;,那么3和"bb"就分别赋值给了x和y;

结构体:分为具名结构体(最常见),元组结构体和单元结构体;

具名结构体写法为struct Peple{name: &str,gender: u32};元组结构体写法为struct Color(i32, i32, i32);,单元结构体则是没有属性的结构体如struct Empty;它等同于struct Empty{}

枚举:枚举有三种形式,分别是无参枚举,类C枚举和类型参数枚举;第一种写法为enum Number {Zero, One},第二种写法为enum Color {Red = 0xff0000},这种的结构体值可以转换为对应的基础类型值;

第三种写法比较特殊,为enum IpAddr {V4(u8, u8, u8, u8)},V4实际上是一个函数类型(不是闭包实现的Fn等的trait,是fn(u8,u8,u8,u8)),枚举值调用方式为Number::Zero;而且枚举值里可以有两种方式,比如enum Option{Some(i32),None},第一个Some(i32)是第三种形式,而None则是第一种形式;

向量:Vec,是容量可变的线性容器,它可以用vec![]初始化一个空向量;用vec![0; 10]初始化一个长度是10初始化值是0的向量;也可以用let mut v3 = Vec::new();来创建一个向量(最好指定类型);

双端队列:VecDeque也是线性容器,它能够从左至右push元素(get为左边最小下标),也能够从右至左push元素;

链表:LinkedList,链表也是类似双端队列,不过它内部是链表来存储元素的;

HashMap:K类型必须是可哈希的类型,V类型则必须是在编译期间已知大小的类型;它和Java的API不同,用insert插入元素;它内部存储的元素是无序的,即每次迭代HashMap输出的内容都会不同;

BTreeMap:约束同上,但是BTreeMap每次插入元素都会自动按Key的值进行排序;【Rust里貌似没有按插入元素的先后顺序排序的Map】

HashSet:其实就是HashMap将value置为()【单元值】的集合;

BTreeSet:原理同上;

BinaryHeap:优先队列,通过二叉最大堆实现的;

待续。。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章