一、变量
声明变量:
变量的定义类似于JS和Swift的定义方式,使用 let 关键字。
//主函数
fn main() {
//定义并初始化一个变量var1
let var1 = 1;
//打印格式化文本并在结尾输出换行
println!("{}", var1);
}
运行 cargo run 命令:输出1。
➜ varandtype git:(master) ✗ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.07s
Running `target/debug/varandtype`
1
不可变变量:
在Rust语言中,所有的变量默认均是不可变变量,不可变变量就是当变量完成值当初始化后不能再次重新赋值的变量。
fn main() {
//默认为不可变变量
let var1 = 1;
println!("{}", var1);
//对不可变变量重新赋值
var1 = 2;
println!("{}", var1);
}
error[E0384]: cannot assign twice to immutable variable `var1`
--> src/main.rs:8:5
|
4 | let var1 = 1;
| ----
| |
| first assignment to `var1`
| help: make this binding mutable: `mut var1`
...
8 | var1 = 2;
| ^^^^^^^^ cannot assign twice to immutable variable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0384`.
error: Could not compile `varandtype`.
修改不可变变量会导致编译不通过,重要提示: cannot assign twice to immutable variable ,而且还给出了修改帮助,从错误提示来看Rust可以说是非常用心,非常给力了。
这里有一点需要注意:当声明变量且未初始化,从程序开始到结束始终未初始化赋值是不允许的。
let var1; //如果只有声明,而始终未赋值是不能编译的
let var1; //声明
var1 = 2; //初始化,这里不是修改,而是初始化变量。
//var1 = 3; 这是修改,是不允许的,因为var1是不可变变量。
可变变量:
可变变量的修饰关键字是 mut ,无论是哪种类型的变量,只有对变量增加了mut修饰才被允许做修改(mut是对变量的修饰,与let无关),这一点是与其他的大部分编程语言不一样的地方,通过mut关键字明确表示这个变量会发生变化,对代码的分析非常有利。
//定义可变变量 mut var1
let mut var1 = 1;
println!("{}", var1); //输出1
var1 = 2;
println!("{}", var1); //输出2
这里省略掉main函数的定义,默认是在main函数的方法体内
静态变量:
静态变量使用 static 关键字声明,而且Rust编译器会建议将变量名大写,静态变量同样是可以用mut修饰为可变变量的,但是定义可变的静态变量是不安全的,只能在 unsafe 修饰的方法或作用区块内使用(读写)可变的静态变量。
//定义一个整型静态变量,静态变量定义时必须要同时初始化并指定类型
static VAR1:i32 = 0;
//定义一个可变静态变量
static mut VAR2:i32 = 0;
//在unsafe作用域中读写VAR2
unsafe {
println!("{}", VAR2); //输出0
VAR2 = 2;
println!("{}", VAR2); //输出2
}
//println!("{}", VAR2); 这里是不被允许的
println!("{}", VAR1); //输出0
全局变量:
全局变量和静态变量很像,都是通过 static 关键字声明,只不过是位于函数之外,在函数之外无法直接使用let定义变量。
//全局变量
static VAR1:i32 = 0;
fn main() {
println!("{}", VAR1);
test();
}
fn test() {
println!("{}", VAR1);
}
常量:
使用 const 定义,并需要明确类型和初始化,因为常量是完全不可修改的,无法使用mut关键字。
const A:i32 = 0;
二、类型
Rust是一种静态类型语言,在运行时就必须确定每一个变量的类型,但是上面的例子中let定义的变量却没有标明类型,这其实是编译器的功劳,编译器在给定字面量值的情况下会自动推导并给出默认的类型。
整型默认的类型就是i32(有符号32位整数)
编辑器使用的是Mac环境下Clion
IDE在变量尾部显示出的虚拟灰色方块就是编译器能够自动推导出来的类型,在编译过程中会明确定义为初始化时的类型。
let mut var1 = 1; //默认i32类型
//var1 = 'a'; 可变变量:值可以变,类型不能变。
下面对变量类型进行了一下整理:
整型:这里比如“i8,i16”等类型,含义是有符号整数,后面的数字代表占用二进制位数,例如:i8,占用8个二进制位,即8bits,一个字节,最高位为符号位,那么数字表示范围则即为闭区间[-128,127]。而无符号整数则不含符号位,全部为正整数。(不明确指定整数类型的变量,默认为 i32 类型)。
浮点型:浮点型默认为 f64 双精度浮点型。
布尔型:布尔类型 bool ,与其他语言一样,值为true和false。
字符型:字符类型与C语言中的字符不同,C语言中的字符仅能够表示ASCII码中的单字节字符,在Rust中, char 类型能够表示一个完整unicode字符,所以是多字节的,占用4字节。
let ch:char = '天';
元组:元组应该不算是数据类型,应该是一系列值的集合,在语法表现形式上与Python中的tuple相似,都是使用括号包含,内部是以逗号分隔每个元素,每个元素类型可以不同,但是与Python不同的是Rust内的元组在增加mut修饰后是可以修改元组内的固有元素的值。
元组内元素的值的个数和值类型在初始化时就确定了。
//定义三个元素的元组
let mut var1:(u8, char, i32) = (1,'1',3);
println!("{}", var1.0); //通过“变量.索引”的形式访问指定元素。
数组:数组同样也是需要在声明时确定数组的长度和类型,数组内元素的类型必须保持一致。
let arr:[i32; 4] = [1, 2, 3, 0];
类型定义内包含元素类型和数组长度,以分号分隔。
向量:向量类型 Vec<T> 使用了泛型,这里T代表需要指定的类型,类似与数组一样,声明一个向量,会在堆空间申请一块内存来存储数据,而数组结构的数据大小固定,仅在栈空间保存数据,这里涉及了一些Rust比较核心的内容“所有权”的概念。
Vec类型有很多可用的方法,具体还要看对应的参考文档。
//创建一个指定类型的空vector
let vector:Vec<i32> = Vec::new();
//vector.push(1); 这句是无法执行的,因为vector不是可变变量。需要使用mut
let v2 = Vec::new();
v2.push(2); //可以执行,而且从创建第一个标量值后,即可确定v2的类型是i32
//v2.push('a'); 此句无法成功编译,类型不正确,
let v3 = vec![]; //通过vec宏创建空的向量
let v4 = vec![1,2,3]; //通过vec宏创建类型为i32的向量,拥有3个元素。
println!("{}", v4[v4.len() - 1]); //访问v4的最后一个元素,输出3;
字符串:字符串在Rust内是一个较为特殊的存在,类似于下面这种:
//属于字面量字符串,值是确定的,类型为 &str
let s:&str = "abc";
这种字面量字符串是一种不可变引用类型, &str 其字符串本身是不可变的。虽然你可以这样定义:
let mut s:&str = "abc";
这只能证明变量 s 是可变的,但"abc"是不变的,那这个引用是从哪里来的呢?这就要说到 String 类型了(其实还有很多其他的字符串类型,先把这个字符串类型理解,其他的就都能迎刃而解了), String 类型是依托于 Vec<T> 类型实现的,创建String的方式与Vec很相似,所以说String类型可以对字符串本身进行修改,包括连接,修改等:
//创建空字符串
let str:String = String::new();
//从abc字面量字符串创建String类型
let str1:String = String::from("abc");
//从abc字面量字符串创建String类型,与上一句作用相同。
let str2:String = "abc".to_string();
//以上字符串不可修改,均为只读,未加mut修饰符。
&str 类型是对String类型的其中一个片段的引用,是一种Slice片段类型,但Slice不能直接使用,需要使用指定类型的表示形式。
let string = "abc".to_string();
//获取string的索引1开始到结尾到片段的引用。
let str:&str = &string[1..];
&var[1..4] 这个语法可以取片段引用,就像js的 slice(),php的array_slice(),python的[1:4]
let arr = [1,2,3,4,5];
let arr_slice = &arr[2..4]; //取片段引用,取到[3,4]
let arr_slice2 = &arr[2..=4]; //取片段引用,取到[3,4,5]
哈希: HashMap<K, V> 可以指定key和value的类型。
//定义key为字符串引用,值为整数,
let mut ha<&str,i32> = HashMap::new();
//新增一个键值对
ha.insert("sd", 2);
println!(" {:?}", ha);
对于Rust内的数据类型远远不止这些,Rust内拥有非常丰富的数据类型及各类自定义类型,如结构体,枚举等,经过一段时间的学习,确实觉得这款语言在安全方面下了很多功夫,在编译器中做了大量的检查工作,将其他语言中令人非常头疼的调试问题(比如指针/变量引用/内存溢出问题),尽量扼杀在编译阶段,虽然这样对开发者的要求要更高一些,需要理解这里面的机制,开发出安全高效的程序。
PS:JetBrains 公司的Clion是非常好用的,电脑硬件条件允许的情况下建议使用一下,不过需要安装Rust插件:
点击Plugins后直接搜索rust即可,图中是安装好的样子,没有安装的可以点击Browse repositories...按钮搜索安装即可。