前言#
上篇文章中我们学了rust的变量、变量的可变性(mutability)、常量以及shadowing。没看过可以看下
今天我们接着往下学。
环境#
还是昨天那个项目
定义变量类型#
rust是一门静态强类型语言,所以任何的变量的类型都需要在编译时确定下来。
与它相反的则是动态弱类型。我们都熟悉的javascript就是,类型是在runtime的时候才确定的,边解析边执行。当然,有typescript[2]之后舒服一些。
你在写js代码的时候是否会经常头疼没有代码提示,比如变量类型、函数用法等?那就是因为js是runtime的时候才进行解析。
所以在rust中,变量需要定义类型。 当然,也不是说非得每个变量都得你去写个类型才行,这多累。rust在编译(compile)的时候会根据你的值(value)推测出你的变量是什么类型的,所以大部分情况下你是不需要去手动写类型的。
但是有的情况它是没法去确定的,还记得我们之前的学习过程中遇到的强制你声明变量类型的报错吗[3]?

这是因为parse可以转换的类型不止一种,可能是num又或者是bool等,这是不符合预期的,所以这里会强制你设置变量的类型,这样runtime的时候才能知道要parse成什么。
定义变量类型很简单,和写typescript一样,只需在变量后等号前: xxx即可。
let num: i32 = str.trim().parse().expect("error"); 标量类型(scalar types)#
可以简单的理解为js中的基础数据类型,比如number、boolean等,也就是简单的数据类型。
在rust中有四种标量类型:integers、floating-point-numbers、Booleans、characters,也就是:整数类型、浮动类型也就是带有小数的数字类型、布尔类型以及字符类型。
integer types#
整数类型,上面parse那行代码中用的i32即是一个整数类型。但如果你看过之前的文章,应该会看到另一个类型u32。实际上他俩都是表示的整数类型。
为什么一个整数有两种类型呢?因为在计算机中负数并不是我们现实生活中的负数,而是一个带符号(二进制最高位置为1)的“正数“。
i就是integer,可用来定义正负数,而u表示unsigned,表示无符号的,只能用来定义正数。32表示32位。 类似的还有如下

其中arch表示的是根据你操作系统来判断,比如64位的就是i64和u64。
integer范围是根据类型定义时的位数来变化的,带符号的正数类型可存储范围为-(2^n - 1)到(2^n - 1) - 1 ,而无符号的只能存储0 - (2^n) - 1范围之间。
比如i8表示-128 ~ 127。而u8则是0 ~ 255。
另外rust中还有一种有趣的写法,不用去定义类型,可以把类型放到value中,比如:
const NUM = 123u8; 123u8 表示数据是123,是u8类型的整数。
另一个有趣的写法是利用下划线把一个数字写的更方便阅读,比如:
const MY_SALARY:isize = 10_000; 10_000和10000是同一个效果,但是可阅读性高了很多。
另外还有一点需要注意,那就是溢出的情况[6]。
开发模式下如果你的数字溢出,会直接崩溃。而生产环境不会,会变成超出的那个值。比如u8类型设置一个266数值,在开发模式下会报错,但是生产环境下会变成9。

最后,整数的默认类型是i32。
floating-point-numbers types#
浮动类型。
有f32、f64以及fsize三种浮动类型。
默认是f64,因为速度快精度高(双精度)。
然后没啥好说的了,该说的integer里面说的差不多了。
二元运算#
数字用的最多的地方自然就是计算,rust中的几种运算和其它语言一致,所以这里简单的过一下即可。

boolean type#
布尔类型
在rust中布尔类型占位一个字节,用bool定义布尔变量
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
} 完了~
characters type#
字符类型
顾名思义,只有是一个字符的时候才算是character。

超了直接报错,另外如果需要设置字符类型得用单引号,不然默认被认为是&str字符串类型。

其它其实也没什么好说的了,和其它语言其实差不多,包含包括ASCII\多种语言字符`emoji`等。
不过需要注意点一点是范围在``U+0000toU+D7FF、U+E000toU+10FFFF`之间,如果不在这个范围内,那就很有可能是乱码。
其实字符并非是一个Unicode的概念,所以在代码中可能会和你的想法有出入[9]。
需要我们留意的一点就是字符串和字符不是同一个东西,字符串有自己的类型,后面会说到,这里就先不说了。
复合类型(compound types)#
标量类型就说完了,现在我们来看下复合类型。
他们的区分其实挺好区分,标量类型只有一种数据又或者是结构,而复合类型允许多个数据或者结构放在同一个变量中。
在rust中有两种原始的复合类型:Array和tuple也就是数组和元组。
tuple#
元组相信大家都不陌生了,可以用来存储各种类型的值,不过有个点需要注意就是元组的长度是固定的,无法在定义后新增或者删除元素之类的。
直接来看下例子
fn main() {
let tuple: (i32, f64, &str, char) = (10, 1.01, "test", '我');
println!("the tuple is {:?}", tuple);
}cargo run一下

是不是很像python的元组?
那么问题来了,要怎么获取其中一个元素。
有两种方式:
- 解构,相信在座的各位都不陌生了,
es6+的方法。
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
} 但是挺蛋疼的是解构需要给出所有的变量才行,比如我只想解构第一个元素,这样是不行的。
- 基于下标
第一种方法有些麻烦,所以基本上都是用的下标获取这种方式。
fn main() {
let tuple: (i32, f64, &str, char) = (10, 1.01, "test", '我');
let first = tuple.0;
println!("{first}");
}元组下标从0开始。
元组的默认值是一个(),表示empty,他们叫他unit,表达式默认返回值就是一个unit,(impilcitly不明显滴)。

Array#
数组相信大家都很熟悉了
不过比起在js中,rust的array被设定为只能包含同一种类型的元素,也就是数组里的元素都得是同个类型。 同样的,它的长度也得是固定的,也是不能新增或者移除元素。
来看下代码
let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];定义元素类型
let a: [i32; 5] = [1, 2, 3, 4, 5];格式为type; len。毕竟类型得一样,长度不可变。
这里还有一种快捷初始化数组的方式
let a = [3; 5]; 格式为[value; len]。等价于[3, 3, 3, 3, 3]。
获取元素的方式和元组差不多
- 通过解构
let arr = [1, 2, 3];
let [a, b, c] = arr;
println!("{a}, {b}, {c}");同样是需要全部解构才行
- 通过下标,也是最常用的方式
fn main() {
let arr = [1, 2, 3];
let a = arr[0];
println!("{a}");
}不过这种方式有一个大问题,那就是数组越界。
数组越界
java中应该是有同样的概念,指的是使用的下标超出数组的长度从而引发的错误。在c/c++中是允许读取超出数组自身长度的,不过数据有没有问题就不是它们的事了。。。
为什么会有这种错误呢?
在rust中,数组是基于stack也就是栈实现的,毕竟空间固定不允许拓展。而栈的空间是连续的,毕竟LIFO。而栈相邻的空间是不知道是啥的,所以这个时候下标超了数据就错了。
例子就不看了,直接看下报错。
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace什么情况下会有这种错误?
当下标是一个变量比如从stdin中获取的,这时就会有可能有问题,而且这个问题并不能在编译过程中发现,这是一个runtime error,因为你并不能确定用户输入了什么(客户在酒吧点了一份炒饭然后酒吧炸了~[14])。
如果想让数组变化的话可以用vector[15],由标准库提供,可改变数组长度等。官方也是很建议用这个vector,如果你不知道用哪个,优先vector。
先来看下如何使用,具体分析后面会说到。
fn main() {
let v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3];
}同样是要求元素类型一样,但是可以伸缩(误)。
新增元素和其他语言差不多,都是push,不过有个点需要注意就是数组自身得是mut的。
总结#
今天我们学习了rust中的标量类型和原始复合类型。
感觉东西很少,但是就是有些累。。。可能是上班遇到了个蛇皮需求。。。
老规矩如果觉得对你有用麻烦点个赞,谢谢~
参考#
- ^rust-data-types https://doc.rust-lang.org/book/ch03-02-data-types.html
- ^typescript https://www.typescriptlang.org/
- ^parse-error https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number
- ^rust-scalar-types https://doc.rust-lang.org/book/ch03-02-data-types.html#scalar-types
- ^rust-integer-types https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-types
- ^rust-integer-overflow https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-overflow
- ^rust-floast-types https://doc.rust-lang.org/book/ch03-02-data-types.html#floating-point-types
- ^rust-bool-type https://doc.rust-lang.org/book/ch03-02-data-types.html#the-boolean-type
- ^rust-string https://doc.rust-lang.org/book/ch08-02-strings.html#storing-utf-8-encoded-text-with-strings
- ^rust-compound-types https://doc.rust-lang.org/book/ch03-02-data-types.html#compound-types
- ^tuple https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
- ^rust-array-type https://doc.rust-lang.org/book/ch03-02-data-types.html#the-array-type
- ^rust-array-invalid-index https://doc.rust-lang.org/book/ch03-02-data-types.html#invalid-array-element-access
- ^经典老梗 https://zhuanlan.zhihu.com/p/355468963
- ^rust-vector https://doc.rust-lang.org/book/ch08-01-vectors.html
编辑于 2022-11-30 16:12・IP 属地广东
