目录
前言
变量绑定
变量遮蔽
常量
基本数据类型
复合数据类型
(1)元组类型
(2)数组类型
(3)结构体类型
(4)枚举类型
总结
前言
上一课是rust的入门第一课,介绍了rust的安装和cargo新建rust项目,这节课我们来了解一下rust的变量和数据类型,rust的变量和其他编程语言略有不同,我们在这一节细说。
在开始介绍之前,我们使用cargo创建项目来编写本节课的代码
cargo init lesson2
使用vscode打开
变量绑定
rust中的变量分为可变和不可变,其他的编程语言中也有这样的概念,比如scala中使用val定义不可变变量,var定义可变变量。不可变变量的含义是第一次赋值后,无法第二次赋值,可变变量则没有这样的限制。
这里需要注意和其他编程语言的变量赋值,变量初始化不同,在rust中这样的行为称为变量绑定,即将变量名称和值绑定在一起,这样做的原因和后面要介绍的所有权机制有很大关系,rust中的所有权是一个很有意思的东西,这里简单说一句:每一块内存都只有一个主人,当主人不在了,这块内存也要交还。想一想垃圾回收是不是也在做这样一件事情,java中jvm不断的寻找没有使用的内存,然后回收这一块内存,rust中的所有权机制使用另一种思路在解决垃圾回收的问题。
fn main() {let x = 8;x = 18;let y: i32 = 18;
}
rust中使用let定义不可变变量,变量名后可以跟数据类型,也可以不跟,编译器会自动推断数据类型,对于不可变变量,进行二次绑定值时会报错,我们使用cargo build编译一下看看报错,可以看到rust的编译器解释的非常详细,第一次将8绑定给变量x,不能把18第二次绑定给x,甚至告诉我们使用mut修饰就解决问题了,不得不感叹rust编译器的强大。
--> src/main.rs:3:5|
2 | let x = 8;| - first assignment to `x`
3 | x = 18;| ^^^^^^ cannot assign twice to immutable variable|
help: consider making this binding mutable|
2 | let mut x = 8;| +++
fn main() {let mut x = 8;x = 18;
}
rust中使用mut修饰可变变量,可以在绑定值后,再次修改变量,重新绑定一个新值,不过要注意,重新绑定的值必须和变量的数据类型一致,即x是i32类型,第二次绑定值,不可以绑定true/false这样的bool值。
变量遮蔽
变量遮蔽是rust中一个不同于其他语言的概念,指的是在同一个作用域中可以再次声明一个之前已经声明过的变量名,新的变量名会遮蔽之前的变量名,之前的变量名不见了,对于第一次学习rust的同学来说很难理解,因为这是其他编程语言中没有的,我们使用代码来解释一下,继续坚持看下去。
fn main() {// 定义不可变量x,给x绑定上内存中的值8let x = 8;// 定义不可变量x,给x绑定上内存中的值truelet x = true;
}
上面代码中,我们使用let定义了2个同名的变量,注意这里不是修改变量的值哦,而是定义了2个不同的变量,其中第一个变量x被第二个变量x遮蔽了,所以既然是不同的变量,数据类型也就可以不一样了,因为这里是2块不同的内存,和之前可变变量的二次绑定不一样,之前的例子中x对应的内存里第一次存的是8,后来存的是18,而变量遮蔽中x对应的内存中第一个存的是8,后面新定义了变量x,对应的是一个新的内存,该内存存的是true,之前8所在的内存还存在,并没有被修改。只是我们使用了相同的变量名称,绑定了一块新的内存空间。那么变量遮蔽的作用是什么呢?显而易见的,可以在不改变变量名的情况下,转换数据类型以及可变性。这也是rust安全性的体现,如果想某个变量的值不再被访问,那就重新定义一个同名的变量吧。
常量
和大部分编程语言的常量一样,rust中常量使用const声明,并且数据类型不能省略,必须显式指出。
fn main() {const PI: f64 = 3.14;
}
基本数据类型
有编程语言基础的,这一小节可以快速浏览,用的时候不断巩固即可
(1)整型
(2)浮点型
(3)布尔型
(4)字符类型
(5)区间类型
范围类型我们说一说,区间类型通常表示为一个表达式,比如(1..5)表示左闭右开的区间[1,5),(1..=5)表示左闭右闭的区间[1,5],这种在循环中,或者初始化一段区间特别方便,这种在别的编程语言也有体现,比如在scala中使用 1 until 5表示左闭右开的区间,使用1 to 5表示左闭右闭的区间,在scala中,这称之为Range。其他的基本类型大家自行了解,简单看看就行,多写写代码熟悉熟悉就好。
复合数据类型
(1)元组类型
元组,tuple,在大部分语言中都有,长度固定,类型可以不同,元组的类型使用(i32, bool)表示,括号中的类型,表示元组中具体数据的类型。因为长度固定,所以不能增删,只能改查,查询使用索引,从0开始。元组是一个非常灵活好用的数据类型,但是java8没有,遇到这种情况就只能定义一个新的类。
fn main() {let mut t1: (i32, f64, bool) = (1, 2.0, true);// 查println!("t1.0 = {}", t1.0);// 改t1.0 = 18;// 定义只有一个元素的元组let t2 = (3,);
}
(2)数组类型
数组,array,在大部分语言中都有,长度固定,类型相同,数组的类型使用[i32;5]表示,分号前表示数组中元素的类型,分号后表示数组的大小。
fn main() {let mut a1: [i32; 4] = [1, 2, 3, 4];// 改a1[0] = 5;// 查println!("a1[0]={}", a1[0]);// 数组的另外一种初始化方式let a2 = [1; 5]; // [1,1,1,1,1]
}
(3)结构体类型
结构体,struct,在java/scala这样面向对象的语言中,一般用类表示,但是类和结构题还是有区别的。结构体的定义和初始化都比较简单,关于结构体的方法的使用和更高阶的用法,我们放在面向对象去讲解,这里只占一个坑,结构体也是rust中的一种数据类型。
关于元组结构体我们也放到后面详述。
fn main() {let s1 = Student {name: "www".to_string(),age: 23,};
}// 定义
struct Student {name: String,age: i8,
}
(4)枚举类型
rust中的枚举非常强大,语法和java等语言中枚举定义类似。下面代码定义了2个枚举类型,一个带参数,一个不带参数。rust中的枚举通常搭配模式匹配使用,关于模式匹配,我们在后面统一讲解,这是一个非常强大的功能,和scala中的模式匹配一样,一旦用上了就爱不释手。
fn main() {let a1 = LEVEL::A(1, "1".to_string());let a2 = LEVEL::A(2, "2".to_string());
}// 定义
enum LEVEL {A(i32, String),B(i32, String),
}enum COLOR {RED,BLUE,
}
总结
本节介绍了rust中的变量和数据类型,需要重点理解有变量绑定,变量遮蔽这两个概念,其他的数据类型和复合数据类型简单过一遍即可,不需要强制记住,这些都是基本语法,结合多年编程的经验来看,基本语法多写写才能巩固,靠看一遍就记住是很难得,语言只是工具,像变量绑定,变量遮蔽代表的是rust设计者的思想,很值得借鉴学习,下一节会详细介绍rust中的容器类型,集合,映射等。