五、类型
Published on
内置类型
标量类型
- 有符号整型:i8, i16, i32, i64, i128, isize
- 无符号整型:u8, u16, u32, u64, u128, usize
- 浮点型:f32, f64
- 字符型:char(4字节Unicode字符)
- 布尔型:bool(true/false)
- unit类型:类型为(),有且仅有一个值()。类似void
复合类型
复合类型有元组与数组两种,它们的长度与其中变量的类型在声明后均不可变。
- 元组:由多个不同类型的变量组成
let x: (i32, f64, u8) = (-1, 2.0, 3); // 声明元组
let a = x.0; // 取元组中的元素
let (a, b, c) = x; // 解构
- 数组:由多个同一类型的变量组成
let a: [i32; 5] = [1, 2, 3, 4, 5]; // 声明数组
let a = [3; 5]; // 使用同一变量初始化
let x = a[0]; // 取数组中的元素
- 切片:使用切片可以更加方便地操作序列数据,但是它本身不包含数据,只是一种引用,包含起始位置与长度两个字段
let a: [i32; 5] = [1, 2, 3, 4, 5]; // 声明数组
let s: &[i32] = &a[1..4]; // 在数组的基础上建立一个切片
自定义类型
结构体(struct)
结构体是用户自定义的变量组合。Rust中有3种类型的结构体:
- 普通结构体:与C类似,为每个字段指定名称,可通过名称访问字段
- 元组结构体:不指定字段的名称,相当于一个有类型名称的元组
- unit结构体:不包含字段,仅使用其类型。一般用于泛型编程
定义
// 普通结构体
struct Point {
x: f32,
y: f32,
}
// 元组结构体
struct Pair(i32, f32);
// unit结构体
struct Unit;
生成实例
// 对于普通结构体,使用键值对的方式
let point: Point = Point { x: 10.3, y: 0.4 };
// 在另一实例的基础上修改少量字段
let bottom_right = Point { x: 5.2, ..point };
// 对于元组结构体,使用类似元组的方式
let pair = Pair(1, 0.1);
// unit结构体
let unit = Unit;
访问字段
// 普通结构体
point.x
// 元组结构体
pair.0
// 解构普通结构体
let Point { x: left_edge, y: top_edge } = point;
// 解构元组结构体
let Pair(integer, decimal) = pair;
方法
可以为结构体添加一些方法,此时结构体类似对象。但是在Rust中,不必在定义结构体时立刻定义全部方法,可以在任意位置为结构体添加方法。方法的第一个参数必须为调用方法的结构体变量自身的引用。
struct Rectangle {
width: u32,
height: u32,
}
// 定义方法
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
// 调用方法
rect1.area()
关联函数
impl块中实现的函数称为关联函数,它没有self参数,相当于面向对象的类方法。
impl Rectangle {
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}
枚举(enum)
枚举类型定义了一组相关的类别标识,每个实例的取值为其中的一个。在Rust中,枚举类型可以只包含类别(类似C中的枚举),还可以额外包含任意数值(类似C中的union)。
简单方式
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
包含数值的方式
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
方法
与结构体类似,也可以为枚举定义方法
类型转换
内置类型转换
Rust中不提供内置类型间的隐式转换,显示转换通过as关键字实现。类型转换与C基本相同,但是在Rust中不会出现未定义的行为,因此更加安全。
let decimal = 65.4321_f32;
let integer = decimal as u8;
自定义类型转换
自定义类型,如结构与枚举,之间进行转换是使用特性完成的。
- From与Into:From特性中的from方法将一种其它类型转换为当前类型,Into中的into方法只是反向执行from操作
- TryFrom与TryInto:与From/Into类似,但是返回值为Result,因此可以处理错误
- ToString与fmt::Display:实现ToString特性以实现将一个类型转换为String;实现fmt::Display特性不仅会自动实现ToString,还可以使用print!宏
- FromStr与parse:一个类型实现FromStr特性后,即可使用String类型的parse函数转换为该类型
高级功能
使用newtype模式
newtype模式是指将一个类型使用枚举结构体封装使其成为另一个类型。newtype模式可用于:
- 为外部类型实现外部特性:外部类型封装后成为内部类型,可以实现外部特性
- 保证类型安全:同一个类型被封装为多个类型,即使本质完全相同,也不能互相代替
- 隐藏实现细节:外部只能看到公共接口,无法看到内部实现;新类型可以提供与内部类型不同的接口
类型别名
使用type语句为类型指定别名。与newtype模式不同,类型别名并不是新类型,它被当作原类型看待。因此别名通常用于避免重复编码较长类型。
type Kilometers = i32;
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
永不返回类型
!是一种特殊的永不返回类型,当一个函数永不返回时,其返回值为!。此类函数称为发散函数。
fn bar() -> ! {
...
}
动态尺寸类型
一般的类型,其尺寸均为固定大小,但是有一些类型其大小并不固定,这就是动态尺寸类型。
最常见的动态尺寸类型是字符串str,所有字符串不可能具有相同的长度。但是,由于长度不固定,并不能直接声明一个str类型的变量。一般使用字符串切片&str,因为切片类型长度固定。也可以将str与各种指针结合,比如Box<str>与Rc<str>.
另一种动态长度类型是特性,所以将特性作为对象时,需要与指针结合,如:&dyn Trait、Box<dyn Trait>或Rc<dyn Trait>。
Rust提供Sized特性表示一个类型尺寸固定。此特性自动为每个尺寸固定的类型实现。此外,该特性自动地作为范围限制为每个泛型函数添加:
// 自动添加Sized特性限制
fn generic<T: Sized>(t: T) {
}
// 可以手动指定可以使用动态尺寸类型,但是只能使用其引用
fn generic<T: ?Sized>(t: &T) {
}