The Rust Programming Language - 第6章 枚举和模式匹配 - 6.1 定义枚举

6 枚举和模式匹配

枚举允许你通过列举可能的成员来定义一个类型。在本章,我们会了解枚举如何连同数据一起编码信息,还会学习一个特殊的枚举Option,另外,我们也会进一步了解match表达式中的模式匹配,针对不同的枚举值编写相应的代码,最后我们将会介绍if let (它是一个结构,能够方便处理代码中的枚举)

枚举是很多语言中都有的一个功能,不同语言功能各不相同。Rust中的枚举与F#、OCaml、和Haskell这样的函数式编程语言中的代数数据类型最为相似

6.1 定义枚举

如果你想把一个所有结果放在一个数据结构中,那么枚举可能是比较合适的选择,比如一个IP地址要么是IPv4要么是IPv6,如下:V4和V6被称为成员

enum IpAddrKind {
        V4,
        V6,
   }

枚举值

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

let seven = 7;

注意:枚举成员须跟在枚举名称后的::后面,与普通的变量声明相比,我们需要把 “枚举名称::成员” 看作成一个整体,它的类型是 IpAddrKind

上面的枚举IpAddrKind 是一个枚举名称,但是我们可以把它当作一个自定义类型,既然把它当作自定义类型了,那它可以干的事情就很多了,我们来看些案例:

当作函数的参数类型

fn route(ip_type:IpAddrKind) {}

理所当然,我们可以使用该类型的值调用该函数

route(IpAddrKind::V4);
route(IpAddrKind::V6);

枚举名称作为类型在结构体中的嵌套

那既然枚举名称可以作为一种类型,就可以把它放在任何一个需要类型的地方,比如现在我们要把它放进结构体中,作为某个成员的类型,形成嵌套结构,我们来看一下实例:

定义结构体

struct IpAddr {
    kind: IpAddrKind,
    address:String,
};

定义两个变量,类型都是上述结构体(要初始化变量):

let home = IpAddr {
    kind:IpAddrKind::V4,
    address:String::from("127.0.0.1"),
};
let lookback = IpAddr {
    kind:IpAddrKind::V6,
    address:String::from("::1"),
};

我们再回顾一下结构体,为什么要定义结构体,一是结构体这种数据结构能够清楚明确表示数据(表示数据关系和数据的意义),二是将结构体作为一个整体在不同的代码模块中使用(比如作为参数传递),三是利用这结构体包含、整合成员的这种思想

让我们继续学习进一步的内容

将数据直接放入枚举

在前面,我们将定义好的枚举作为类型放入了结构体种作为了成员的类型,接着我们又将结构体实例化并且作为值赋给了变量,通过这一些列的操作,我们可以将实例了的数据保存在变量中

现在我们的目的仍然是保存这些实例值,但现在我们不使用结构体这个中间变量,而是直接把数据放入枚举(感觉是把立体的数据压平了,这点读者可以自行体会)

enum IpAddrKind {//定义有哪几类
        V4,
        V6,
   }
   
enum IpAddr {//定义每个类具体值是什么(多层级划分的话还可以继续嵌套)
        V4(String),
        v6(String),
   }
   
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::v6(String::from("::1"));

那么我们小结一下这样做的好处是什么?一当然是更简单,省去了中间结构体,二是容易修改,如下,我们想修改某个成员的类型

enum IpAddr {
        V4(u8,u8,u8,u8),
        v6(String),
    }
let home = IpAddr::V4(127,0,0,1);
let loopback = IpAddr::v6(String::from("::1"));

将结构体嵌套枚举中

前面,结构体中可以放入枚举类型,那反过来,结构体能放入枚举中吗?答案是可以,事实上,同结构体一样,枚举中可以放入任何类型的数据,我们一起来看几个例子

标准库中对IpAddr 的处理

struct Ipv4Addr {
    // -- snip --
}
struct Ipv6Addr {
    // -- snip --
}
enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),   
}

将其他类型嵌套在枚举中

enum Massage {
    Quit,//未关联任何数据
    Move {x:i32,y:i32},//包含一个匿名结构体
    Write(String),//包含一个单独的String
    ChangeColor(i32,i32,i32),//包含3个i32
}

上面的枚举中每个成员都包含了不同的值和类型,并且定义像上面这样的枚举其实是定义如类似的结构体很像,我们再把上述枚举中的成员拆开写一下

struct Quitmassage;
struct MoveMassage {
    x:i32,
    y:i32,
};
struct WriteMessage(String);
struct ChangeColorMessage(i32,i32,i32);

在枚举上定义方法

impl Message {
    fn call(&self) {
        //定义方法体
    }
}
let m = Message::Write(String::from("hello"));
m.call();

这个例子中,创建了一个值为 Message::Write(String::from("hello")) 的变量 m,而且这就是当 m.call() 运行时 call 方法中的 self 的值

Option枚举和其相对于空值的优势

Option是标准库定义的一个枚举,它代表了最普遍的应用场景,要么有值要么无值

Rust并没有很多其它语言有的空值功能,空值Null是一个值,它代表没有值。在有空值地语言中,变量总是这两种状态之一:空值和非空值

空值尝试表达的概念是有意义的:空值是一个因为某种原因无效或缺失的值

然而问题不在于概念而在于具体实现,Rust并没有空值,不过它确实有一个可以编码存在或不存在概念的枚举,这个枚举就是Option,而且它定义于标准库中,如下:

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

Option非常重要以至于它被包含到了prelude中,因此我们不需要显式的引入作用域,对它的成员也是如此,可以不需要Option::前缀来直接使用Some(T)和None。但是即便如此,Option也是常规枚举,Some(T)和None也仍然是它的成员

语法我们还没有讲到,它是一个泛型类型参数,我们在第十章会讲到泛型。目前,我们仅需要知道意味着Option枚举的成员Some可以包含任意类型的数据,下面是一些例子:

fn main {
    let some_number = Some(5);
    let some_string = Some("a string");

    let absent_number = Option<i32>= None;//如果使用None而不是Some,需要告诉Rust<T>是什么类型的,因为编译器只通过None值无法推断出Some成员保存的值的类型
}

当有一个Some值时,我们就知道存在一个值,而这个值保存在Some中,当有个None值时,某种意义上, 它跟空值具有相同的意义,并没有一个有效的值,那为什么Option就比空值要好呢

简而言之,Option和T是不同的类型(这里的T可以是任何类型),编译器不允许像一个 肯定有效的值 那样使用Option ,例如Option + i8类型数据会出现错误

let x:i8 = 5;
    let y:Option<i8> = Some(5);

    let sum = x+y;

    println!("{}",sum)//no implementation for `i8 + Option<i8>`

换句话说,要对Option 进行T运算时,必须将其转换为T,通常这能帮助我们捕获空值最常见的问题之一:假设某值不为空,但是实际上为空的情况

为了处理一个可能为空的值,你必须将其显式的放入其对应类型的Option中,接着,当使用这个值时,必须明确处理值为空的情况,只要一个值不是Option类型,你就可以安全的认定它不为空

那么当有一个Option的值时,如何从Some成员中取出T的值来用它呢?可以查看这个文档:https://doc.rust-lang.org/std/option/enum.Option.html

总而言之,为了使用Option值,需要编写处理每个成员的代码关于如何处理,具体内容我们在下一节中进一步了解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值