首页 > 编程语言 >rust程序设计(6)枚举与模式匹配

rust程序设计(6)枚举与模式匹配

时间:2023-11-19 16:24:01浏览次数:37  
标签:rust i32 模式匹配 枚举 let match 类型 Rust

rust中的枚举有什么用?枚举可以嵌入类型的好处是什么

  • 你可以在同一个枚举中既有单个值,也有元组或结构体。
  • 枚举的每个变体可以拥有不同数量和类型的关联数据。
  • 这增加了类型的灵活性和表达力,使你能够更精确地建模你的数据。

我知道rust可以为枚举创建方法,那在哪种场景下枚举会比结构体会有优势

  • 表示多个互斥状态
  • 封装多种不同的类型,并且这些类型共享相同的方法
  • 模式匹配

枚举应用场景示例

场景

假设我们正在构建一个图形用户界面(GUI)应用程序,需要表示一个界面元素(如按钮、标签、或复选框)的不同类型。每种界面元素都有一些共同的属性(如位置和大小),但也有一些特定于类型的属性和行为。

使用结构体的方法

我们可以为每种元素类型定义一个结构体,但这种方法在处理共同属性和类型特定逻辑时会有些冗余。

struct Button {
    position: (i32, i32),
    size: (i32, i32),
    label: String,
    // ... Button特定属性和方法
}

struct Label {
    position: (i32, i32),
    size: (i32, i32),
    text: String,
    // ... Label特定属性和方法
}

// ... 更多元素类型

使用枚举的方法

使用枚举,我们可以更优雅地封装这些不同的元素类型,同时保持公共属性的一致性。

enum GuiElement {
    Button {
        position: (i32, i32),
        size: (i32, i32),
        label: String,
        // ... Button特定属性
    },
    Label {
        position: (i32, i32),
        size: (i32, i32),
        text: String,
        // ... Label特定属性
    },
    // ... 更多元素类型
}

impl GuiElement {
    fn draw(&self) {
        match self {
            GuiElement::Button { position, size, label } => {
                // 绘制按钮的逻辑
            },
            GuiElement::Label { position, size, text } => {
                // 绘制标签的逻辑
            },
            // ... 其他元素的绘制逻辑
        }
    }
}

优势

在这个例子中,使用枚举的优势包括:

  • 减少冗余:所有GUI元素共享的属性(如位置和大小)在枚举的每个变体中都得到了保留,减少了代码重复。
  • 统一处理:可以在枚举上实现方法(如draw),这些方法可以处理所有不同类型的元素,使得代码更加整洁。
  • 模式匹配:利用Rust的模式匹配能力,可以轻松地针对不同的枚举变体执行不同的逻辑。

这个例子展示了当你有多种类型,它们具有一些共同属性但也有自己独特的特征和行为时,枚举是一个很好的选择。

  • 那枚举和继承或者组合这种设计模式之间是什么关系呢?

    枚举与继承的关系

    1. 替代继承:在一些OOP语言中,继承用于创建一组相关的子类,每个子类都扩展或修改了基类的行为。Rust不支持传统的类继承,但枚举可以用来实现一种类似的效果。你可以创建一个枚举,其每个变体代表不同的类型,这些类型在概念上相似但在行为上有所不同。
    2. 类型多态性:尽管Rust没有类似Java或C++中的类层次结构,但通过枚举和模式匹配,你可以构建出一种多态性。不同于通过基类指针或引用调用派生类的方法,Rust使用模式匹配来根据枚举的不同变体执行不同的代码。

    枚举与组合的关系

    1. 组合的体现:在OOP中,组合是一种将不同对象组合在一起以构建更复杂对象的方法。在Rust中,枚举可以包含不同的数据类型,包括结构体,这在某种程度上反映了组合的思想。枚举变体可以嵌入不同的数据和结构,从而构建出多样化的数据结构。
    2. 灵活性:组合在OOP中被认为是一种比继承更灵活的设计方法。在Rust中,使用枚举嵌入不同的数据结构同样提供了高度的灵活性,允许你根据不同的需求组合数据和行为。

那rust中的枚举是否更类似于设计模式中的组合呢?

枚举与组合模式的相似之处

  1. 封装多种类型:就像组合模式允许对象将多个对象组合成树状结构来表示部分与整体的层次结构,Rust的枚举也允许在一个单一的数据类型中封装多种不同的类型。枚举的每个变体可以代表不同的数据类型,类似于组合模式中的不同节点。
  2. 统一接口:组合模式中,不同的对象共享一个公共接口。类似地,在Rust中,不同的枚举变体可以通过枚举类型的方法来处理,使得对不同数据的处理具有统一性。
  3. 灵活的数据结构:组合模式提供了创建复杂对象的灵活方式,而Rust的枚举也提供了定义复杂数据类型的灵活性。你可以在枚举中定义各种复杂的数据结构,包括嵌套的枚举和结构体。

枚举与组合模式的差异

  1. 模式匹配与多态性:Rust的枚举结合模式匹配提供了一种不同于传统OOP的多态性。枚举允许你在运行时基于变体的不同执行不同的代码,这与组合模式中的对象树和动态分派有本质的不同。
  2. 用途与范围:组合模式主要用于设计对象的部分与整体层次结构,而Rust的枚举更多是用于表示一个实体可以拥有的一系列互斥的状态或类型。枚举的使用范围更广泛,不仅限于构建层次结构。
  3. 语言特性与范式:Rust的枚举体现了函数式编程元素,特别是与模式匹配结合使用时,而组合模式是一种面向对象的设计模式。这两者反映了不同的编程范式和语言特性。

结论

虽然Rust的枚举在某些方面与组合模式相似,特别是在封装多种类型和创建灵活数据结构方面,但它们在设计目的、使用方式和潜在的范式上有所不同。Rust的枚举更多地被用于表示数据的不同变体,以及在类型层面上实现多态性和模式匹配,这些用途超出了传统组合模式的范畴。

Rust 中的空值 Option

为了拥有一个可能为空的值,你必须要显式的将其放入对应类型的 Option<T> 中。接着,当使用这个值时,必须明确的处理值为空的情况。只要一个值不是 Option<T> 类型,你就 可以 安全的认定它的值不为空。这是 Rust 的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加 Rust 代码的安全性。

在 Rust 中,确实没有传统意义上的 "null" 值,这是为了避免空指针异常这类常见的错误。相反,Rust 使用 Option 枚举来显式处理可能的值缺失情况。当您使用 Option 类型的值时,Rust 强制您必须先检查是否有值,然后再使用它,这样可以确保您的代码在处理可能缺失的值时更加安全和可靠。

用Option处理空值的一个示例:

fn find_even_number(numbers: Vec<i32>) -> Option<i32> {
    for number in numbers {
        if number % 2 == 0 {
            return Some(number);
        }
    }
    None
}

fn main() {
    let numbers = vec![1, 3, 5, 7, 10, 11];
    match find_even_number(numbers) {
        Some(even) => println!("找到了偶数: {}", even),
        None => println!("没有找到偶数"),
    }
}

在这个示例中,find_even_number 函数在一个整数数组中寻找第一个偶数。如果找到偶数,它返回 Some(even_number);如果没有找到,它返回 None。在 main 函数中,通过 match 表达式来处理这个 Option 值。这确保了在使用找到的偶数之前,已经检查过它是否存在。

模式匹配

match 的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。可以把 match 表达式想象成某种硬币分类器:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会通过 match 的每一个模式,并且在遇到第一个 “符合” 的模式时,值会进入相关联的代码块并在执行中被使用。

使用 if let 意味着编写更少代码,更少的缩进和更少的样板代码。然而,这样会失去 match 强制要求的穷尽性检查。match 和 if let 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。换句话说,可以认为 if let 是 match 的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。

fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max {
        println!("The maximum is configured to be {}", max);
    }
}

练习题

练习题 1:定义枚举

根据《Rust 编程语言》(中文版)第 6 章第 1 节的内容,请定义一个名为 TrafficLight 的枚举,它应该包含三种变体:RedYellowGreen

练习题 2:使用 match 表达式

针对练习题 1 中定义的 TrafficLight 枚举,请编写一个函数 traffic_light_time,该函数接受一个 TrafficLight 枚举类型的参数,并返回该交通灯颜色对应的等待时间(以秒为单位)。使用 match 表达式来实现这一功能。例如,红灯可能对应 30 秒,黄灯 3 秒,绿灯 45 秒。

练习题 3:使用 if let

假设有一个 Option<i32> 类型的值,使用 if let 来检查该值是否为 Some(3)。如果是,则打印出 "值为 3",否则不打印任何东西。

练习题 4:综合应用

创建一个 enum,名为 FileState,包含 OpenClosedError(String) 这三种状态。然后编写一个函数,该函数接受 FileState 枚举并使用 match 表达式来处理不同的情况。例如,如果文件处于打开状态,打印 "文件已打开";如果文件关闭,打印 "文件已关闭";如果有错误,打印错误信息。

练习题 1 答案:定义枚举 TrafficLight

enum TrafficLight {
    Red,
    Yellow,
    Green,
}

// 这只是枚举的定义,不需要进一步的代码来实现功能。

练习题 2 答案:使用 match 表达式

fn traffic_light_time(light: TrafficLight) -> u32 {
    match light {
        TrafficLight::Red => 30,
        TrafficLight::Yellow => 3,
        TrafficLight::Green => 45,
    }
}

// 测试代码
fn main() {
    let light = TrafficLight::Red;
    println!("等待时间: {} 秒", traffic_light_time(light));
}

练习题 3 答案:使用 if let

fn check_option_value(value: Option<i32>) {
    if let Some(3) = value {
        println!("值为 3");
    }
}

// 测试代码
fn main() {
    let value = Some(3);
    check_option_value(value);
}

练习题 4 答案:综合应用 FileState

enum FileState {
    Open,
    Closed,
    Error(String),
}

fn handle_file_state(state: FileState) {
    match state {
        FileState::Open => println!("文件已打开"),
        FileState::Closed => println!("文件已关闭"),
        FileState::Error(msg) => println!("错误: {}", msg),
    }
}

// 测试代码
fn main() {
    let file_state = FileState::Error(String::from("无法访问文件"));
    handle_file_state(file_state);
}

这些答案是基于您所学习的博客内容的直接应用。它们不仅帮助您理解 Rust 枚举、match 表达式和 if let 的具体用法,同时也为您提供了实际的编码实践。

标签:rust,i32,模式匹配,枚举,let,match,类型,Rust
From: https://www.cnblogs.com/azoux/p/17842184.html

相关文章

  • rust程序设计(5)结构体相关练习题| 附带解答
    题目基础结构体练习:创建一个名为Person的结构体,包含name(字符串类型)和age(整数类型)两个字段。写一个函数,接收一个Person实例作为参数,并打印出这个人的名字和年龄。结构体方法练习:为Person结构体添加一个方法birthday,当调用时,它将这个人的年龄增加1。创建一个Person实例......
  • C枚举类型
    ......
  • 28_rust_无畏并发
    无畏并发Concurrent:程序不同部分之间独立执行;Parallel:程序不同部分同时运行。rust无畏并发:允许编写没有细微Bug的代码。并在不引入新Bug的情况下易于重构。这里所说的“并发”泛指concurrent和parallel。使用线程同时运行代码1:1模型:实现线程的方式:通过调用OS的API创建线程。......
  • rust程序设计(4)关于 trait | impl 相关的概念和疑问
    trait是什么?Rust中的trait是一种定义可被多种类型实现的共享行为的方式。它类似于Java或C#中的接口。通过trait,你可以定义一组方法签名(有时包括默认实现),不同的类型可以实现这些方法。这有助于抽象通用功能并确保不同类型间一定程度的一致性。当一个类型实现了一个trait,它承诺提......
  • java-Junit 注解 枚举
    第15章_Junit_注解_枚举Junit单元测试引入【1】软件测试的目的:软件测试的目的是在规定的条件下对程序进行操作,以发现程序错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程。 【2】测试分类:(1)黑盒测试:软件的黑盒测试意味着测试要在软件的接口处进行。这种方法是......
  • 正则表达式工具:强大且高效的模式匹配利器
    https://www.cnblogs.com/Amd794/p/17813641.htmlhttps://amd794.com/regularGraph正则表达式,或称为regex,是一种强大的文本处理工具,它以特定的字符串模式匹配为基础,并有能力进行复杂的搜索、编辑和操作。尽管其语法可能初看起来复杂,但掌握正则表达式将使您能够以高效且准确的方......
  • Windows rustup update 速度慢,使用字节跳动Rust镜像加速
    不设置镜像加速rustup更新升级会非常慢RsProxy字节跳动的Rust镜像 Windows想要使用这个镜像需要按照官方提示去设置两个系统变量分别为 RUSTUP_DIST_SERVER RUSTUP_UPDATE_ROOT 之后来到当前用户文件夹下修改cargo的配置文件(没有就创建一个)C:\Users\你PC名\.c......
  • @各大高校|亚洲诚信TrustAsia接入CARSI,四大福利活动重磅来袭!
    亚洲诚信TrustAsiaEduPKI在CARSI平台正式上线,为广大CARSI成员校师生提供SSL证书和专业的技术服务支持,守卫高校安全!伴随着人工智能、大数据、物联网等新一代数字化技术的迅猛发展,教育信息化2.0和智慧校园建设得到快速推进。但与此同时,勒索软件、钓鱼邮件等网络安全威胁层出不穷,这对......
  • esp32笔记[10]-rust驱动ssd1306显示屏
    摘要使用rust(no-std)环境和esp-hal库实现SSD1306显示屏(128x64)显示bmp图片.平台信息esp32(模组:ESP32-WROOM-32D)(xtensalx6)(xtensa-esp32-none-elf)rust超链接esp32笔记[7]-使用rust+zig开发入门原理简介rust的include_bytes!宏Rust的include_bytes!宏可以用......
  • 蓝桥杯之模拟与枚举day1
    Question1卡片(C/C++A组第一题)这个是一道简单的模拟枚举题目,只要把对应每次的i的各个位都提取出来,然后对应的卡片数目减去1即可。属于打卡题目。注意for循环的特殊使用即可#include<iostream>usingnamespacestd;boolsolve(inta[],intn){//模拟枚举while(n!=0)......