rust枚举

2023-09-21 23:01:57

一、定义枚举

1.使用enum关键字定义枚举。
语法格式如下

enum enum_name {
     variant1,
     variant2,
     variant3
}

例如

enum Fruits {
     Banana, // 香蕉
     Pear, // 梨
     Mandarin, // 橘子
     Eggplant // 茄子
}

2.可以为枚举成员添加属性

enum Book {
     Papery(u32),
     Electronic(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));

如果你想为属性命名,可以用结构体语法:

enum Book {
     Papery { index: u32 },
     Electronic { url: String },
}
let book = Book::Papery{index: 1001};

二、使用枚举

(一)使用::
语法格式如下

enum_name::variant

例如

let selected = Fruits::Banana;
let selected: Fruits = Fruits::Banana;

范例

#[derive(Debug)]
enum Fruits {
     Banana, // 香蕉
     Pear, // 梨
     Mandarin, // 橘子
     Eggplant // 茄子
}
fn main() {
     let selected = Fruits::Banana;
     println!("{:?}",selected);
}
编译运行结果如下
Banana

(二)判断枚举
只能使用match语句判断枚举

enum CarType {
     Hatch,
     Sedan,
     SUV
}
fn print_size(car:CarType) {
     match car {
          CarType::Hatch => {
               println!("Small sized car");
          },
          CarType::Sedan => {
               println!("medium sized car");
          },
          CarType::SUV =>{
               println!("Large sized Sports Utility car");
          }
     }
}
fn main(){
     print_size(CarType::SUV);
     print_size(CarType::Hatch);
     print_size(CarType::Sedan);
}
编译运行结果如下
Large sized Sports Utility car
Small sized car
medium sized car

带属性的枚举

#[derive(Debug)]
enum GenderCategory {
     Name(String),
     Usr_ID(i32)
}
fn main() {
     let p1 = GenderCategory::Name(String::from("Mohtashim"));
     let p2 = GenderCategory::Usr_ID(100);
     println!("{:?}",p1);
     println!("{:?}",p2);
     match p1 {
          GenderCategory::Name(val)=> {
               println!("{}",val);
          }
          GenderCategory::Usr_ID(val)=> {
               println!("{}",val);
          }
     }
}
编译运行结果如下
Name("Mohtashim")
Usr_ID(100)
Mohtashim

三、标准库枚举

标准库内置了很多枚举

(一)Option
这个枚举是Option<T>,而且它定义于标准库中,如下:

enum Option<T> {
     Some(T),
     None,
}

它有两个枚举值 None 和 Some(T)。
None 表示可有可无中的无。
Some(T) 表示可有可无中的有,既然有,那么就一定有值,那个T就表示值的类型。

1.定义枚举值
Option<T> 枚举包含在了prelude之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以直接使用Some和None,不需要加Option:: 前缀。

定义枚举值,可以使用Some(value)或者None。
例子:

let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;

如果使用None,需要指定Option<T> 是什么类型的,因为编译器只通过None无法推断出Some成员的类型。

当有一个Some值时,我们就知道存在一个值,而这个值保存在Some中。当有个None值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。
那么,Option<T> 为什么就比空值要好呢?
当在Rust中拥有一个像i8这样类型的值时,编译器确保它总是有一个有效的值。我们可以自信使用而无需做空值检查。只有当使用Option<i8>的时候需要担心可能没有值,而编译器会确保我们在使用值之前处理了为空的情况。
换句话说,在对Option<T> 进行T的运算之前必须将其转换为T。通常这能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况。
不再担心会错误地假设一个非空值,会让你对代码更加有信心。为了拥有一个可能为空的值,你必须要显式地将其放入对应类型的Option<T> 中。接着,当使用这个值时,必须明确地处理值为空的情况。只要一个值不是Option<T> 类型,你就 可以 安全地认定它的值不为空。这是Rust的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加Rust代码的安全性。

2.使用枚举
(1)获取some的值
expect
如果是Some,就返回Some的值。如果是None就打印消息,并崩溃。
Panics
如果值为 None 并带有由 msg 提供的自定义panic消息,就会出现panics。
例子

let x = Some("value");
assert_eq!(x.expect("fruits are healthy"), "value");

let x: Option<&str> = None;
x.expect("fruits are healthy"); // `fruits are healthy` 的panics

unwrap
如果是Some,就返回Some的值。如果是None就崩溃。
由于此函数可能会panic,因此通常不建议使用该函数。 应该使用match显式处理None,或者调用unwrap_or,unwrap_or_else,unwrap_or_default。
Panics
如果self的值等于 None,就会出现panics。
例子

let x = Some("air");
assert_eq!(x.unwrap(), "air");
let x: Option<&str> = None;
assert_eq!(x.unwrap(), "air"); //崩溃

unwrap与expect一样,唯一不同是expect能打印自定义消息,而unwrap不能

(2)判断是否有值
is_some
如果选项是 Some 值,则返回 true。
例子

let x: Option<u32> = Some(2);
assert_eq!(x.is_some(), true);
let x: Option<u32> = None;
assert_eq!(x.is_some(), false);

is_none
如果选项是 None 值,则返回 true。
例子

let x: Option<u32> = Some(2);
assert_eq!(x.is_none(), false);
let x: Option<u32> = None;
assert_eq!(x.is_none(), true);

(3)逻辑运算
and
如果自己为 None,则返回 None; 否则,返回参数。
例子

let x = Some(2);
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

let x: Option<u32> = None;
let y = Some("foo");
assert_eq!(x.and(y), None);

let x = Some(2);
let y = Some("foo");
assert_eq!(x.and(y), Some("foo"));

let x: Option<u32> = None;
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

or
如果自己有值,则返回自己,否则返回参数。
例子

let x = Some(2);
let y = None;
assert_eq!(x.or(y), Some(2));

let x = None;
let y = Some(100);
assert_eq!(x.or(y), Some(100));

let x = Some(2);
let y = Some(100);
assert_eq!(x.or(y), Some(2));

let x: Option<u32> = None;
let y = None;
assert_eq!(x.or(y), None);

xor
如果自己和参数之一恰好是Some,则返回 Some,否则返回 None。
例子

let x = Some(2);
let y: Option<u32> = None;
assert_eq!(x.xor(y), Some(2));

let x: Option<u32> = None;
let y = Some(2);
assert_eq!(x.xor(y), Some(2));

let x = Some(2);
let y = Some(2);
assert_eq!(x.xor(y), None);

let x: Option<u32> = None;
let y: Option<u32> = None;
assert_eq!(x.xor(y), None);

(二)Result
Option枚举返回 None 可以表明失败。但是有时要强调为什么会失败。为做到这点,我们提供了 Result 枚举类型。

enum Result<T, E> {
     Ok(T),
     Err(E),
}

Result<T, E> 有两个取值:
Ok(value) 表示操作成功,并包装操作返回的 value(value 拥有 T 类型)。
Err(why),表示操作失败,并包装 why,它能够解释失败的原因(why 拥有 E 类型)。
与Option枚举一样,Result枚举和其成员也被导入到了prelude中,所以就不需要在match分支中的Ok和Err之前加Result::。

1.定义枚举
使用Ok(value)和Err(why)
例子

let ok = Ok(5);
let err = Err("not a string");

2.使用枚举
(1)获取Ok的值
expect
返回包含 self 值的包含的 Ok 值。
由于此函数可能为panic,因此通常不建议使用该函数。 应该使用match显式处理 Err,或者调用 unwrap_or,unwrap_or_else,unwrap_or_default。
Panics
如果值为 Err,就会出现panics,其中panic消息包括传递的消息以及 Err 的内容。
例子

let x: Result<u32, &str> = Ok(2);
assert_eq!(x.expect("expect a num"), 2);

let x: Result<u32, &str> = Err("emergency failure");
x.expect("Testing expect"); // `Testing expect: emergency failure` 的panics

unwrap
返回包含 self 值的包含的 Ok 值。
由于此函数可能为panic,因此通常不建议使用该函数。 应该使用match显式处理Err,或者调用unwrap_or,unwrap_or_else,unwrap_or_default。
Panics
如果该值为 Err,就会出现Panics,并由 Err 的值提供panic消息。
例子

let x: Result<u32, &str> = Ok(2);
assert_eq!(x.unwrap(), 2);

let x: Result<u32, &str> = Err("emergency failure");
x.unwrap(); // `emergency failure` 的panics

unwrap_or_else

use std::fs::File;
use std::io::ErrorKind;
fn main() {
     let f = File::open("hello.txt").unwrap_or_else(|error| {
         if error.kind() == ErrorKind::NotFound {
             File::create("hello.txt").unwrap_or_else(|error| {
                 panic!("Problem creating the file: {:?}", error);
             })
         } else {
             panic!("Problem opening the file: {:?}", error);
         }
     });
}

(2)判断是否是ok
is_ok
如果结果为 Ok,则返回 true。
例子

let x: Result<i32, &str> = Ok(-3);
assert_eq!(x.is_ok(), true);
let x: Result<i32, &str> = Err("Some error message");
assert_eq!(x.is_ok(), false);

is_err
如果结果为 Err,则返回 true。
例子

let x: Result<i32, &str> = Ok(-3);
assert_eq!(x.is_err(), false);
let x: Result<i32, &str> = Err("Some error message");
assert_eq!(x.is_err(), true);

(3)转换错误
ok
Result<T, E> 转换为 Option<T>
将 self 转换为 Option<T>,使用 self,并丢弃错误 (如果有)。
Examples

let x: Result<u32, &str> = Ok(2);
assert_eq!(x.ok(), Some(2));
let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.ok(), None);

err
Result<T, E> 转换为 Option<E>
将 self 转换为 Option<E>,使用 self,并丢弃成功值 (如果有)。
Examples

let x: Result<u32, &str> = Ok(2);
assert_eq!(x.err(), None);
let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.err(), Some("Nothing here"));

(4)函数返回错误,称为传播错误
例子

use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
     let f = File::open("hello.txt");
     let mut f = match f {
         Ok(file) => file,
         Err(e) => return Err(e),
     };
     let mut s = String::new();
     match f.read_to_string(&mut s) {
         Ok(_) => Ok(s),
         Err(e) => Err(e),
     }
}

这种传播错误是如此的常见,以至于Rust提供了 ?运算符来简化操作。
?运算符用在返回值为Result的表达式后面,它等同于这样一个match表达式:其中Err(err)分支展开成return Err(err),而Ok(ok)分支展开成ok。
例子

use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
     let mut f = File::open("hello.txt")?;
     let mut s = String::new();
     f.read_to_string(&mut s)?;
     Ok(s)
}

可以在 ? 之后直接使用链式方法调用来进一步缩短代码

use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
     let mut s = String::new();
     File::open("hello.txt")?.read_to_string(&mut s)?;
     Ok(s)
}

? 运算符只能用于返回Result的函数
下面代码编译错误

use std::fs::File;
fn main() {
     let f = File::open("hello.txt")?;
}

有两种方法修复这个问题。一是将函数返回值类型修改为Result<T, E>。二是不使用?运算符。
main函数是特殊的,其返回类型是有限制的。main函数的一个有效的返回值是 (),另一个有效的返回值是Result<T, E>,如下所示:

use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
     let f = File::open("hello.txt")?;
     Ok(())
}
更多推荐

分享从零开始学习网络设备配置--任务3.5 使用静态路由实现网络连通

任务描述某公司规模较小,该公司的网络管理员经过考虑,决定在公司的路由器、交换机与运营商路由器之间使用静态路由,实现网络的互连。静态路由一般适用于比较简单的网络环境。在这样的环境中,网络管理员应非常清楚地了解网络的拓扑结构,以便于设置正确的路由信息。由于该网络规模较小且不经常变动,所以使用静态路由比较合适。任务要求(1)

网络安全(黑客)自学

前言:想自学网络安全(黑客技术)首先你得了解什么是网络安全!什么是黑客网络安全可以基于攻击和防御视角来分类,我们经常听到的“红队”、“渗透测试”等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面性,例如Web安全技术,既有Web渗透,也有

【Oracle】Oracle系列之五--Oracle表空间

文章目录往期回顾前言1.基本概念2.表空间的创建与管理(1)表空间的创建(2)修改表空间数据文件大小(3)表空间不足时,增加数据文件(可增加1个或多个)(4)重命名表空间数据文件(5)删除表空间往期回顾【Oracle】Oracle系列–Oracle数据类型【Oracle】Oracle系列之二–Oracle数据字典【Or

【数据库系统概论】数据库的四个基本概念:数据、数据库、数据库管理系统和数据库系统

数据(data)数据库(DataBase,DB)数据库管理系统(DataBaseManagementSystem,DBMS)数据库系统(DataBaseSystem,DBS)感谢💖数据(data)定义:数据是描述事物的符号记录。数据是数据库中存储的基本对象。描述事物的符号可以为数字、文本、图像、音频、视频等。数据库(

【Linux】系统编程线程互斥与同步(C++)

目录【1】线程互斥【1.1】进程线程间的互斥相关背景概念【1.2】互斥量mutex【1.3】互斥量实现原理探究【1.4】RAII的加锁风格【2】可重入VS线程安全【2.1】概念【2.2】常见的线程不安全的情况【2.3】常见的线程安全的情况【2.4】常见不可重入的情况【2.5】常见可重入的情况【2.6】可重入与线程安全联

如何从外网远程控制企业内网电脑?

在企业中,保护公司机密和数据安全是至关重要的。为了确保员工在使用公司电脑时遵守相关规定,许多公司会采取外网监控员工电脑的方法。本文将介绍一些真实有效的方法和具体的操作步骤,以帮助您更好地监控员工电脑。一、什么是外网监控?外网监控,又称为远程桌面监控,是指通过网络连接到员工电脑,实时查看员工电脑的操作情况,以便对员工的工

NLP(5)--自编码器

目录一、自编码器1、自编码器概述2、降噪自编码器二、特征分离三、自编码器的其他应用1、文本生成2、图像压缩3、异常检测四、VAE1、极大似然估计2、GSM3、GMM4、VAE的引出5、VAE一、自编码器1、自编码器概述自编码器(Auto-Encoder)作为无监督学习的一种算法,自编码器中包含Encoder和Decod

UEFI 安装 Debian12 Linux 物理机虚拟机VMware通用

文章目录前言⭐前置虚拟机物理机安装流程选择安装方式语言及键盘选择网络选择创建用户系统磁盘分区新旧磁盘分区方式BOOT分区SWAP分区根分区安装过程中其他选项选择软件包安装流程末前言⭐物理机和虚拟机安装仅有设置UFFI引导的差别、这里前置为设置UEFI引导。安装步骤大同小异,理解思路即可轻松完成安装配置。前置虚拟机1.首

Maven的介绍和使用

Maven的作用项目构建依赖管理:避免资源间版本冲突问题统一开发结构:提供统一的项目结构Maven的使用下载完压缩包之后放在合适的目录下,其中apache-maven-3.8.8文件夹是安装的maven,下面的repository是本地仓库,其中要修改setting.xml下的仓库路径,设置阿里云镜像。将这里的路径改为

【C++】AVL树

AVL树1.AVL树的概念2.AVL树的实现2.1节点的定义2.2插入2.3是否是AVL树3.AVL树与红黑树1.AVL树的概念AVL树是一棵二叉搜索树,但它的每个节点的左右子树的高度差的绝对值不超过1,且它的子树也是平衡二叉树。左右子树的高度差也叫平衡因子,平衡因子=右子树叶的高度-左子树的高度。将AVL树与满二叉树

代理IP与Socks5代理:跨界电商智能爬虫的引擎与安全壁垒

摘要:随着跨界电商的蓬勃发展,数据采集和隐私保护成为企业的关键挑战。本文将深入探讨代理IP和Socks5代理在跨界电商中的应用,以及它们在智能爬虫技术中的关键作用,为企业提供数据引擎和安全防护的解决方案。第一部分:背景介绍跨界电商已成为全球贸易的重要组成部分,为企业提供了巨大的市场机会。然而,要在这个竞争激烈的领域中脱

热文推荐