【Rust学习之旅】变量和基础类型数据(二)

1,330 阅读7分钟

上一期我们讲到了rust的环境配置与安装,简单了了解了一下cargo的简单使用,并成功运行了第一个rust经典程序hello world。这一期我们说一下学习一门语言,最基础的知识,变量和基础类型。

变量可变性

rust奇怪的言论:变量默认是不可改变的(immutable), 这是不是逻辑错误🤔️

变量与常量

这里我们来理解一下,变量与常量,这里和javascript,有很多区别不同的地方。

变量

一个变量的声明rust使用let 关键词,熟悉js的小伙伴可能很熟悉,但是区别的是let声明的变量是不可以二次赋值的,就像js里的const 而且更严格.我们使用cargo run运行以下代码,编译器就会就会跑出错误,不能对不可变变量 x 二次赋值cannot assign twice to immutable variable `x` ),因为你尝试对不可变变量 x 赋第二个值。

fn main() {
    let x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

那它为什么又是变量勒,因为rust默认变量是不可变的,当我们需要变量可变的时候,我们需要一个新的修饰符mut .

fn main() {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

我们在刚才的代码上中let关键字后面加了mut关键字,现在运行程序就可以正确输出结果了。

常量

常量rust使用const关键字声明,类似于不可变变量,常量 (constants)  是绑定到一个名称的不允许改变的值,那和我们刚才使用 let 声明的变量有什么区别。

  • 不允许对常量使用 mut。常量不光默认不可变,它总是不可变。
  • 必须注明值的类型
  • 常量只能被设置为常量表达式,而不可以是其他任何只能在运行时计算出的值。

Rust 对常量的命名约定是在单词之间使用全大写加下划线,这是一个约定行为,我们在写Typescript,常量也有类似约定。

fn main() {
   const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
}

变量的隐藏

我们可以定义一个与之前变量同名的新变量。这样第一个变量被第二个 隐藏(Shadowing)  了,这意味着当您使用变量的名称时,编译器将看到第二个变量。实际上,第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 let 关键字来多次隐藏。

刚才我们说变量默认不可变,那这种行为是不是就是可变了?隐藏与mut有什么区别?

隐藏与将变量标记为 mut 是有区别的。

  • 当再次使用 let 时,实际上创建了一个新变量,我们可以改变值的类型,并且复用这个名字。mut是不可以改变值的类型的
  • 当不小心尝试对变量重新赋值时,如果没有使用 let 关键字,就会导致编译时错误。
  • 通过使用 let,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。

基础数据类型

Rust 是 静态类型statically typed)语言,也就是说在编译时就必须知道所有变量的类型。

在 Rust 中,每一个值都属于某一个 数据类型data type),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)复合(compound)

javascript里也有类似概念值类型引用类型,但是这里有很大的区别,并不是根据存放在,的位置划分的。

根据值及其使用方式,编译器通常可以推断出我们想要用的类型。但是当多种类型均有可能时,比如使用 parse 将 String 转换为数字时,必须增加类型注解,不然编译器就会抛出错误。

fn main() {
let guess: u32 = "42".parse().expect("Not a number!");
}

标量类型

Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。

整型

整数 是一个没有小数部分的数字。有符号整数类型以 i开头, 无符号整数是 u开头。(有符号 和 无符号 代表数字能否为负值)

长度有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常是个不错的起点,数字类型默认是 i32isize 或 usize 主要作为某些集合的索引。

浮点型

Rust 也有两个原生的 浮点数 类型,它们是带小数点的数字。Rust 的浮点数类型是 f32 和 f64,分别占 32 位和 64 位。默认类型是 f64,因为在现代 CPU 中,它与 f32 速度几乎一样,不过精度更高。所有的浮点型都是有符号的。

浮点数采用 IEEE-754 标准表示。f32 是单精度浮点数,f64 是双精度浮点数。

布尔值

这里就和javascript完全一致了,true,flase

字符类型

用单引号声明 char 字面量、使用双引号声明字符串字面量。

这里和javascipt不一样,对单引号与双引号做了区分,单引号一般是单个字符,双引号是一个字符串

复合类型

复合类型Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:数组(array)、元组(tuple)

数组类型

这里是的数组和我们在javascript中的数组不完全一致了

数组中的每个元素的类型必须相同、Rust 中的数组长度是固定的。

可以像这样编写数组的类型:在方括号中包含每个元素的类型,后跟分号,再后跟数组元素的数量。

fn main() {
let a: [i32; 5] = [1, 2, 3, 4, 5];
}

还可以通过在方括号中指定初始值加分号再加元素个数的方式来创建一个每个元素都为相同值的数组:

fn main() {
let a = [3; 5];
}

变量名为 a 的数组将包含 5 个元素,这些元素的值最初都将被设置为 3。这种写法与 let a = [3, 3, 3, 3, 3]; 效果相同,但更简洁。

取值和正常数组取值一样 ,通过下标取就行了a[0]

注意:rust 数组是可以在栈 (stack) 上分配的已知固定大小的单个内存块。这里和javascript不一样,引用类型数据在栈空间中是存储堆空间的地址,在堆空间存储栈空间的值

元组类型

元组就是将多个类型的只组合在一起,有点像javascript的对象。

但是需要注意

  • 元组长度固定:一旦声明,其长度不会增大或缩小。
  • 使用包含在圆括号中的逗号分隔的值列表来创建一个元组。
fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

既然我们说它像数组,它当然也支持javascript中的解构赋值

fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;

    println!("The value of y is: {y}");
}

我们也可以使用点号(.)后跟值的索引来直接访问, 元组的第一个索引值是 0。

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}

不带任何值的元组有个特殊的名称,叫做 单元(unit)  元组。这种值以及对应的类型都写作 (),表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。

结语

看完了这里基础类型都没有提到类似于javascript中的对象,和可变长度的数组, 不用担心,后面我们会讲,vector 类型结构体

这里可以暂且先抛弃掉javascript中对象就是一切的观念

本文正在参加「金石计划」