• 大致了解了一下rust语言的一些语法,现在来正式学习一下rust

变量与常量

  • 变量与常量是通用的编程概念,所以基本上介绍一下rust中变量的声明,常量的声明就可以了。这里具体介绍一下变量的可变性,以及这个性质与常量的区别
  • 变量与常量中将介绍:
    • 使用let关键字声明一个变量,声明的变量默认不可变
    • 使用mut关键字跟在let关键字后面,变量名的前面,表示声明的变量是可变类型
    • 使用constant关键字声明一个常量
    • 变量声明的隐藏机制

变量与可变性

变量的声明

  • 我们可以使用let关键字来声明一个变量,这里编译器会自动判断类型,我们也可以指定数据类型。
  • 例如:
1
2
3
4
5
6
fn main() {
let a = 1;
let b : u32 = 2;
println!("a = {}",a);
println!("b = {}",b);
}

image-20241114141735213

变量的不可变

  • 在rust中,使用let关键字声明的变量默认不可改变,如果我们在代码中修改了不可变的变量,在编译的时候编译器就会报错。例如:
1
2
3
4
5
6
fn main() {
let x = 5;
println!("The value of x is: {}",x);
x = 6;
println!("The value of x is: {}",x);
}
  • 当我们使用cargo run先将该代码进行编译时,编译器就会出现报错,红色框的意思就是不能分配两次不可变的x

image-20241114142219717

变量的可变

  • 在声明变量的时候可以使用mut关键字,可以使得不可变的变量变成可变的例如:
1
2
3
4
5
6
fn main() {
let mut a = 5;
println!("The value of x is: {}",a);
a = 6;
println!("The value of x is: {}",a);
}
  • 使用cargo run命令,编译并运行后就会出现如下结果:

image-20241114145732306

常量

  • 常量就是一个不可变的量。并不能使用mut关键来修饰常量。
  • 我们可以定义一个常量,将一个常量或者常量表达式直接绑定到常量上。注意:常量不能绑定返回值
  • 接下来是常量的例子:
1
2
3
4
5
6
fn main() {
const MAX_POINTS: u32 = 10000;
const ABC_AAA: u32 = MAX_POINTS + 10;
println!("MAX_POINTS = {}",MAX_POINTS);
println!("ABC_AAA = {}",ABC_AAA);
}
  • 编译运行后的结果为:

image-20241114151104952

变量和常量的区别

  • 变量的和常量的声明关键字是不一样的。
  • 变量可以使用mut改变可变性,常量始终是不可以被改变的。

隐藏

  • 变量还有一个特性就是隐藏特性,接下来从一个例子中具体体会一下变量声明的隐藏特性。
  • 在这里我们先声明了变量x,将其赋值为5。之后又声明了一个同名的变量x,将其赋值为x+1这样新声明的x就会被赋值为6。而就的变量就被隐藏了(注意这里是是利用了关键字let之后再赋值,不是像之前一样没有使用mut关键字,让变量变成可变的直接赋值)
1
2
3
4
5
6
7
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;

println!("The value of x is: {}",x);
}
  • 编译运行该程序会出现如下结果:

image-20241114152314868

  • 这里具体介绍一下变量的隐藏机制:
    • 声明新的变量可以覆盖掉旧的同名变量,这种在rust中被称为shadow
    • 我们可以重复使用let关键字并配以相同的名称来不断地隐藏变量

数据类型

  • 数据类型被划分为标量类型和复合类型。
  • rust是编译型语言,这意味着它在编译程序的过程中需要知道所有变量的具体类型。

标量类型

整数类型

  • 整数类型主要学习整数类型的声明和整数类型的表示

整数类型的声明

  • 整数类型,分为有符号整数和无符号整数,并且整数类型可以用不同的位来表示。
  • 这些整数类型都会标明自身是否存在符号,并且拥有一个明确的大小。
  • 这里注意一下:iszieusize这俩个类型是分别表示有符号数和无符号数,但是长度是由运行该程序的计算机架构所决定,如果是在64位计算机运行,长度就为64位;在32为计算机运行,长度就为32位。

image-20241128102041760

  • 接下来试着指定类型声明变量:
1
2
3
4
5
6
7
8
9
10
11
fn main() {
let a: u8 = 15;
let b: u16 = 30;
let c: u32 = 60;
let d: i8 = -1;
let e: i16 = -4;
let f: i32 = -8;
let g: i64 = -90;
let h: usize = 100;
println!("a is {} \nb is {} \nc is {} \nd is {} \ne is {} \nf is {} \ng is {} \nh is {}",a,b,c,d,e,f,g,h);
}
  • 输出结果如下:

image-20241128103455151

整数类型的表示

  • 对于整数类型的表示,可以直接使用一串数字,也可以使用_作为分隔符以方便读数,比如:1_000
  • 还可以像其他语言一样,将整数类型改为其他进制类型进行表达。

image-20241128104514096

  • 接下来尝试编写下,这些形式,对这些表达做一个熟悉。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let a: u32 = 1000000;
let b: u32 = 1_000_000;
println!("a is {}\nb also is {}",a,b);
let hex1: u32 = 0xffff;
let hex2: u32 = 0xff_ff;
println!("hex1 is {}\nhex2 also is {}",hex1,hex2);
let oct1: u32 = 0o7777;
let oct2: u32 = 0o777_7;
println!("oct1 is {}\noct2 also is {}",oct1,oct2);
let bin1: u32 = 0b11111111;
let bin2: u32 = 0b1111_1111;
println!("bin1 is {}\nbin2 also is {}",bin1,bin2);
let chr1: u8 = 65;
let chr2: u8 = b'A';
println!("chr1 is {}\nchr2 also is {}",chr1,chr2);
}
  • 运行结果如下:

image-20241128105831617

浮点数类型

  • 浮点类型,浮点类型也就是带小数的类型。
  • 浮点类型分为俩种类型,f32f64这俩种类型,这俩个类型分别占用32位、64位。
  • 在rust语言中,编译器默认会把浮点数字面量推导为f64类型。
  • 接下来尝试声明一下浮点数
1
2
3
4
5
fn main() {
let a = 2.5; // 编译器推导为f64类型
let b: f32 = 2.6; // 指定为f32类型
println!("a = {} is f64\nb = {} is f32",a,b);
}
  • 运行结果如下:

image-20241128110647899

数值运算

  • 整数和浮点数类型的运算,rust中支持常见的运算:加、减、乘、除、取余。
  • 注意:rust基本运算并没有乘方运算,要实现乘法运算需要调用标准库中的pow方法

  • 这里也熟悉一下基本运算:

1
2
3
4
5
6
7
8
9
10
fn main() {
let a: u32 = 63;
let b: u32 = 13;
let sum = a + b;
let difference = a - b;
let product = a * b;
let quotient = a / b;
let remainder = a % b;
println!("sum = {}\ndifference = {}\nproduct = {}\nquotient = {}\nremainder = {}",sum,difference,product,quotient,remainder);
}
  • 输出结果:

image-20241128112243074

布尔类型

  • rust中的布尔类型和其他语言一样,也只有两种可能的值:truefalse
  • 布尔类型占用一个字节的大小,可以使用bool关键字来显式的声明布尔类型。也可以使用truefalse编译器会推导为bool类型
  • 尝试声明布尔类型:
1
2
3
4
5
fn main() {
let t = true;// 编译器推导为bool类型
let f: bool = false;// 显式声明为bool类型
println!("t is {}\nf is {}",t,f);
}
  • 运行结果如下:

image-20241128113213191

字符类型

  • rust中,char类型被用于描述语言最基础的单个字符。
    • char类型使用单引号指定,而不同于字符串的双引号指定。
    • rust中的char类型占4个字节,是Unicode标量值(即char是Unicode编码)
    • 所以rust中的char类型可以表示比ASCII码多得多的字符。字母,中文,日文,韩文,零长度空白字符,emoji表情。
  • 接下来声明一下字符类型:
1
2
3
4
5
fn main() {
let c = 'z';
let z = '😀';
println!("c = {} \nz = {}",c,z);
}
  • 运行结果:

image-20241128115946625

复合类型

元组类型

  • 主要介绍元组的声明、元组的解构、访问元组的元素

  • 元组是一个复合类型,它可以将不同标量类型的多个值复合在一起

  • 元组的长度是固定的,元组声明结束后无法增加或减少元组其中的元素的量。

元组的声明

  • 元组是用小括号包裹起来,并且使用逗号将元组中的每个值隔开。
  • 元组中的每个位置都对应一个类型,并且这些类型可以相同也可以不同。

  • 例:

1
2
3
4
5
6
fn main() {
let tup1 = (500,6.4,'1'); // 隐式的说明元组中的数据类型,由编译器推导
let tup: (i32,f64,char) = (500,6.4,'😀'); // 显式的声明元组中的数据类型
println!("tup1 is {},{},{}",tup1.0,tup1.1,tup1.2);
println!("tup0 is {},{},{}",tup.0,tup.1,tup.2);
}
  • 运行结果如下:

image-20241129202645176

元组元素的访问

  • 在元组变量中,我们可以使用点标记法,后接元素的索引号(默认从0开始到n-1)
  • 使用点标记法我们可以获取元组中单个元素的值,上段代码中我们输出元组的值就是使用电标记法
  • 例如:
1
2
3
4
5
6
7
8
9
fn main() {
let tup1 = (500,6.4,'1');
let tup: (i32,f64,char) = (500,6.4,'😀');
println!("tup1 is {},{},{}",tup1.0,tup1.1,tup1.2);
println!("tup0 is {},{},{}",tup.0,tup.1,tup.2);

let tup2: (i32,u32,char) = (-1,6,'😀');
println!("tup2 is {},{},{}",tup2.0,tup2.1,tup2.2);
}
  • 运行结果:

image-20241129202902387

元组的解构

  • 我们可以定义元组类型的变量,将元组类型的变量,将一个元组赋值给该变量,这个变量元组的中的各个变量的值,就会被赋上相应的值。
  • 例如:
1
2
3
4
5
fn main() {
let tup1: (u32,i32,char) = (1,-1,'a');
let (_x,y,_z) = tup1;
println!("This is the value of y:{}",y);
}
  • 输出结果:

image-20241129203700861

  • 这里还要注意一点,当我们代码是如下这样的时候,会出现警告,但是最终还是能运行。这个警告可以使用let (_x,y,_z) = tup1;消除,这个警告产生的原因是x,z只是声明了,并没有在代码中被使用,这样会造成空间上的浪费,所以发出警告
1
2
3
4
5
fn main() {
let tup1: (u32,i32,char) = (1,-1,'a');
let (x,y,z) = tup1;
println!("This is the value of y:{}",y);
}

image-20241129203849021

数组类型

  • 介绍数组类型的声明访问数组元素

数组的声明

  • 数组同样是可以存储多个值的复合数据类型,但是数组只能存储数据类型相同的多个值。
  • rust中的数组也是拥有固定长度的,一旦声明了就没办法改变长度了。
  • rust的标准库中提供了一种更灵活的动态数组类型(vector类型),它允许用户自由的改变长度。
  • 数组的声明如下:
    • 使用方括号括起来,并且数组中的每个元素用逗号隔开
    • 还可以像显式表示数据类型那样,变量后跟冒号,使用方括号括起来,前面表示数据类型,后面表示数据元素个数,俩者之间使用冒号隔开。
    • 还可以使用类似于初始化数组的语法来声明数组。与第二种声明方式比较像。只不过前面不是数据类型,而是具体的值
1
2
3
4
5
fn main(){
let a = [1,2,3,4,5,6];
let b: [i32;5] = [1,2,3,4,5];
let c = [3,5]; // 等价于 let c = [3,3,3,3,3];
}

数组元素的访问

  • 数组元素的访问与C语言一样,都是数组名[i]
  • 例如:
1
2
3
4
5
6
fn main() {
let a = [1,2,3,4,5,6];
let first = a[0];
let seconed = a[1];
println!("first:{}\nseconed:{}",first,seconed);
}
  • 运行结果如下:

image-20241129205451210

函数

  • 基本上编程语言都会存在函数,说到函数就离不开函数的定义、函数的参数、函数的返回值

函数的声明

  • 函数的声明是通过关键字fn来声明的,关键字fn后面跟着一个函数的名称,然后名称之后使用()来表示函数,最后在{}内表示函数的具体执行过程。函数名使用的是蛇形命名法,具体命名方法在下面rust名称约定再谈
  • rust函数的声明与c函数的声明不同,不需要事先声明,可以在main函数之后直接声明和定义。也可以将自定义的函数放在main函数之前定义
  • 例如:
1
2
3
fn main(){
let _a = 3;
}
  • 又例如:
1
2
3
4
5
6
7
8
fn main() {
println!("Hello,world!");
another_function();
}

fn another_function(){
println!("Another function.");
}
  • 运行结果如下:

image-20241130204656905

函数的参数

  • 在函数声明中可以定义参数,它们被视为函数的一部分,所以我们在自定义函数的时候就可以使用上参数。在英文中参数这个抽象的概念被称为``

rust名称约定

变量名约定

  • rust中声明一个变量的时候都是按照蛇形命名法进行命名的

    • 变量中所有字母都小写
    • 单词之间使用下划线分隔
    • 常用在大多数普通变量、函数名、模块名
  • 例如:

1
2
3
4
5
let user_name = "iyheart";
let user_age = "18";
fn sun_a_b(){

};
  • 这里注意一下:当一个变量名被声明,但是该变量名没有被使用,编译器会发生警告,如果要消除警告,就要使用如下方式声明变量:在变量名前添加一个_表示该变量没有被使用,这样编译器就不会发出警告了。
1
let _a = 1;

常量名约定

  • 以下划线分隔的全大写字母来命名一个常量,并在数值中插入下划线来提高可读性
  • 例如:
1
const MAX_POINTS: u32 = 100_000