Wang's blog

五、类型

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) {
}