前面我们讲解了 Rust 变量的相关内容,其中涉及到了变量类型,今天就来介绍一下基础类型吧。
Rust 包含以下几种基础类型:
- 布尔类型
- 字符类型
- 整数类型
- 浮点数类型
- 数组类型
- 切片类型
- 元组类型
- 字符串类型
下面我们就来逐个地进行介绍:
一、布尔类型
布尔类型使用 bool 关键字来表示,它仅有两个值:true 和 false ,这和大多数编程语言都相同:
fn main() {
// 自动推断为bool类型
let is_real = true;
if is_real {
println!("is real");
}
// 显式声明为bool类型
let is_real: bool = false;
if !is_real {
println!("not real");
}
}
二、字符类型
字符类型使用 char 关键字来表示,代表一个 Unicode 编码的字符,它占用 4 个字节,即 32 位,可涵盖世界范围内的所有语言文字。字符值须使用两个单引号来包裹:
fn main() {
// 自动推断为char类型
let a = 'a';
// 显式声明为char类型
let b: char = 'b';
println!("{} {}", a, b);
// 中文
let zh = '好';
println!("{}", zh);
// Unicode特殊字符
let super_z = 'ℤ';
// Unicode图案
let super_cat = '????';
println!("{} {}", super_z, super_cat);
}
需要注意的是,字符类型要求在声明字面量时,必须为其指定一个具体的字符,若不指定,编译时将会报错:
fn main() {
// error: empty character literal
let empty = '';
println!("{}", empty);
}
三、整数类型
在 Rust 中,整数类型又分为 有符号整数 和 无符号整数 两个部分。
1. 有符号整数类型(signed integer)
有符号整数类型包括以下几种:
- i8,占用 8 位(即 1 个字节),范围在 [-128, 127] 之间,相当于 Java 中的 byte 类型;
- i16,占用 16 位(即 2 个字节),范围在 [-2^15, 2^15 - 1] 之间,相当于 Java 中的 short 类型;
- i32,占用 32 位(即 4 个字节),范围在 [-2^31, 2^31 - 1] 之间,相当于 Java 中的 int 类型;
- i64,占用 64 位(即 8 个字节),范围在 [-2^63, 2^63 - 1] 之间,相当于 Java 中的 long 类型;
- i128,占用 128 位(即 16 个字节),范围在 [-2^127, 2^127 - 1] 之间;
- isize,根据平台决定存储位数,在 32 位平台下占用 32 位,在 64 位平台下占用 64 位;
由于有符号整数的最高位,被用来存储当前数值的符号,所以真实值的存储位只有 n - 1 位。
如果我们在声明变量时,没有指定类型,编译器会默认使用 i32 类型,我们也可以显示地声明类型:
fn main() {
// 自动推断为i32类型
let a = 1;
// 显式声明为i32类型
let b: i32 = 3;
// 根据平台来决定存储位数
let c: isize = 5;
println!("{} {} {}", a, b, c);
}
2. 无符号整数类型(unsigned integer)
无符号整数类型包括以下几种:
- u8,占用 8 位(即 1 个字节),范围在 [0, 255] 之间;
- u16,占用 16 位(即 2 个字节),范围在 [0, 2^16] 之间;
- u32,占用 32 位(即 4 个字节),范围在 [0, 2^32] 之间;
- u64,占用 64 位(即 8 个字节),范围在 [0, 2^64] 之间;
- u128,占用 128 位(即 16 个字节),范围在 [0, 2^128] 之间;
- usize,根据平台决定存储位数,在 32 位平台下占用 32 位,在 64 位平台下占用 64 位;
由于无符号整数的所有位数,都是用来存储真实值的,所以它只能表示正整数,也正是因为这个,它的正数可表示范围,要比有符号整数大一些。
在声明无符号整数时,必须显式地指明类型信息,原因很简单,如果没有显示声明,就会被编译器推断为 i32 类型:
fn main() {
let a: u32 = 1;
let b: u64 = 3;
let c: usize = 5;
println!("{} {} {}", a, b, c);
}
以上就是整数类型,接下来我们来介绍浮点数类型。
四、浮点数类型
Rust 中的浮点数遵循 IEEE 754 标准,包含 单精度浮点数 和 双精度浮点数 两种类型:
- f32,单精度浮点数,占用 32 位(即 4 个字节),相当于 Java 中的 float 类型;
- f64,双精度浮点数,占用 64 位(即 8 个字节),相当于 Java 中的 double 类型;
在声明浮点型变量时,如果不指定类型,编译器会默认使用 f64 类型,所以如果我们想定义一个 f32 类型的变量,则需要显示声明类型信息:
fn main() {
// 默认为f64类型
let a = 1.0;
// 显式声明为f32类型
let b: f32 = 3.0;
println!("{:?} {:?}", a, b);
}
五、数组类型
数组在 Rust 中是一种 元素类型统一 、容量大小固定 的数据结构,一旦声明,数组的长度和元素的类型都不可再更改。
下面是几种常见的数组定义方式:
fn main() {
// 编译器推断为[i32; 5]类型
let a = [1, 2, 3, 4, 5];
// 显式声明类型信息
let b: [i32; 5] = [1, 2, 3, 4, 5];
println!("{:?} {:?}", a, b);
// 包含5个元素 每个元素初始化值为1
let c = [1; 5];
// 等价于下面这种方式
let d = [1, 1, 1, 1, 1];
println!("{:?} {:?}", c, d);
}
从上面代码可以看出,数组的类型由两部分组成:[elem_type; length],即元素类型和数组长度。
在表示数组元素时,可以直接以字面量形式列出所有元素,也可以指定元素初始值和数组长度,即 [init_value; length] 这种形式。
如果我们想更改数组内的元素值,则必须为数组加上 mut 关键字修饰:
fn main() {
// 加上mut才允许更改数组元素
let mut a = [1, 2, 3, 4, 5];
// 将第一个元素改为6
a[0] = 6;
// 打印结果[6, 2, 3, 4, 5]
println!("{:?}", a);
}
如果两个数组的 元素类型 和 长度 都相同,则可以相互赋值:
fn main() {
let mut a = [1, 2, 3, 4, 5];
// 打印结果[1, 2, 3, 4, 5]
println!("{:?}", a);
let b = [3; 5];
// 成功赋值
a = b;
// 打印结果[3, 3, 3, 3, 3]
println!("{:?}", a);
}
反之,如果类型和长度有任何一项不相同,则两个数组是不同的类型,赋值将会导致编译报错。
最后,数组提供了 len() 和 iter() 两个方法,我们可以利用它们,来迭代数组内部的元素,下面对比了两种迭代方式的不同:
fn main() {
let ary = [5, 6, 7];
// 通过索引迭代
for i in 0..ary.len() {
println!("{}", ary[i]);
}
// 直接迭代元素
for elem in ary.iter() {
println!("{}", elem);
}
}
六、切片
切片是在源数据集基础之上创建的的引用(或称为视图),它包含了源数据集在指定范围内的子区间数据。
下面我们使用数组作为源数据集,演示一下如何创建切片:
fn main() {
let ary = [5, 6, 7];
// 推断为&[i32]类型
let a = &ary[..];
// 显式声明为&[i32]类型
let b: &[i32] = &ary[..];
println!("{:?} {:?}", a, b);
}
在上面的代码中,我们可以看得出来,切片的类型是 &[T] ,其中 T 表示数据集元素的类型。
另外,我们使用 &ary[..] 这样的语法获取一个切片,它表示当前切片是基于 ary 这个数据集创建的,子区间的范围是 0..ary.len(),其中对于 & 符号,我们可以理解为一个引用。
切片的区间范围有很多种表示方式,我们来看一下:
fn main() {
let ary = [6, 7, 8, 9, 10];
// 所有元素 [6, 7, 8, 9, 10]
let a = &ary[..];
// 索引1处开始 [7, 8, 9, 10]
let b = &ary[1..];
// 索引3处停止 [6, 7, 8]
let c = &ary[..3];
println!("{:?} {:?} {:?}", a, b, c);
// 范围[1, 3) 结果[7, 8]
let d = &ary[1..3];
// 范围[1, 3] 结果[7, 8, 9]
let e = &ary[1..=3];
println!("{:?} {:?}", d, e);
}
切片的迭代和数组很相似,不同的是,切片也可以直接进行元素的迭代:
fn main() {
let ary = [5, 6, 7];
let slice = &ary[..];
// 与数组类似 迭代索引
for i in 0..slice.len() {
println!("{}", i);
}
// 与数组类似 迭代元素
for elem in slice.iter() {
println!("{}", elem);
}
// 可直接迭代元素
for elem in slice {
println!("{}", elem);
}
}
七、元组类型
元组是一组固定容量的有序列表,和数组不同,元组对元素的类型不做要求,所以我们能在元组中存放各种类型的数据:
fn main() {
// 声明一个元组
let a = (500, 6.4, 1);
// 显式声明类型信息
let b: (i32, f64, u8) = (500, 6.4, 1);
println!("{:?} {:?}", a, b);
}
元组可以通过解构来获取到内部的元素:
fn main() {
let a = (500, 6.4, 1);
// 解构
let (x, y, z) = a;
println!("{:?} {:?} {:?}", x, y, z);
}
另外,元组还提供了以索引的方式访问内部元素:
fn main() {
let a = (500, 6.4, 1);
// 通过索引的方式
let x = a.0;
let y = a.1;
let z = a.2;
println!("{:?} {:?} {:?}", x, y, z);
}
和数组类似,通过索引我们也可以更改元组在指定索引处的值:
fn main() {
// 加上mut修饰
let mut a = (500, 6.4, 1);
// 更改第一个元素的值
a.0 = 600;
// 获取最新值
let x = a.0;
let y = a.1;
let z = a.2;
// 打印结果600 6.4 1
println!("{:?} {:?} {:?}", x, y, z);
}
和数组类似,相同类型的元组之间也是可以相互赋值的:
fn main() {
// 加上mut修饰
let mut a = (500, 6.4, 1);
// 打印(500, 6.4, 1)
println!("{:?}", a);
let b = (600, 6.5, 2);
// 成功赋值
a = b;
// 打印(600, 6.5, 2)
println!("{:?}", a);
}
八、字符串类型
当我们谈到 Rust 的字符串时,会涉及到以下两个概念:
- 原生字符串:类型可表示为
&str ,容量大小固定,一旦声明就不可更改。
- 标准库字符串:即 std::string::String 类型,容量可变,内容可更改,并且能与原生字符串相互转化。
下面我们来演示一下,如何声明这两种字符串:
fn main() {
// 推断为&str类型
let a = "hello";
// 显式声明为&str类型
let b: &str = "hello";
// 转为标准库的String类型
let c: String = b.to_string();
// 通过&str创建String类型变量
let mut d: String = String::from(b);
// 有mut修饰 才可进行追加
d.push_str(" rust");
// 追加一个字符
d.push('!');
println!("{} {} {} {}", a, b, c, d);
}
关于字符串,这里不做过多讲解,接下来我们会用专门的篇幅进行介绍。
以上就是 Rust 基础类型的相关内容了,基础类型是一门编程语言的重要组成部分,希望大家都可以熟练地掌握这些内容。
|
请发表评论