rust枚举

一、定义枚举

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(())
}
相关推荐
wowocpp2 分钟前
ubuntu 22.04 server 格式化 磁盘 为 ext4 并 自动挂载 LTS
服务器·数据库·ubuntu
wclass-zhengge5 分钟前
Netty篇(入门编程)
java·linux·服务器
方方怪10 分钟前
与IP网络规划相关的知识点
服务器·网络·tcp/ip
weixin_442643421 小时前
推荐FileLink数据跨网摆渡系统 — 安全、高效的数据传输解决方案
服务器·网络·安全·filelink数据摆渡系统
Karoku0661 小时前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
半桶水专家1 小时前
用go实现创建WebSocket服务器
服务器·websocket·golang
布值倒区什么name2 小时前
bug日常记录responded with a status of 413 (Request Entity Too Large)
运维·服务器·bug
。puppy3 小时前
HCIP--3实验- 链路聚合,VLAN间通讯,Super VLAN,MSTP,VRRPip配置,OSPF(静态路由,环回,缺省,空接口),NAT
运维·服务器
颇有几分姿色3 小时前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
EricWang13584 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端