从C++到Rust (三):枚举类型

和大多数编程语言一样,Rust使用 enum 关键字定义枚举类型,用来表示一些数量有限的类型,例如常见的使用一个枚举类型表示星期几:

enum Week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}

与C++相比,enum语法上的不同主要体现在两点:

  1. 在传统的C/C++中,枚举类型在编译后会退化为int类型,因此枚举类实际可以直接和Int进行比较和相互转换;Rust中枚举类则是一个真正的独立类型(类似于C++11中的强类型枚举),无法直接与其他类型进行运算;
  2. 默认情况下,Rust的枚举类型是无法直接进行比对运算的(即无法使用“==”运算符),常见的做法是使用match语句对枚举进行匹配
 enum Week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}
 
 let today = Week::Mon;
 
//默认情况下,这种写法会编译报错
//if today == Week::Mon{
//    println!("Today is Monday");
//}

match today {
    Week::Mon =>{
        println!("Today is Monday!");
    }
    _ => {
        println!("Today is Not Monday");
    }
};

Rust枚举最精妙的设计在于,Rust允许枚举绑定一个特定类型的数据成员

来看一个业务中常见的场景:在不同组件之间,我们通常使用Message或者Command的形式在组件间通讯,并且往往需要在消息中传递一些额外的参数。例如,假设我们有一个组件负责显示相关的工作,我们给这个组件传递的消息内容可能有以下几类:

  1. 开、关显示,需要附加一个布尔值,表示开关状态;
  2. 调整显示亮度,需要附加一个数字,表示亮度值
  3. 调整分辨率,需要附加两个数据,表示分辨率
  4. 显示特定的内容,附加一个字符串表示需要显示的字符串

如果尝试为这个场景设计API,你会发现很难用一种简单而统一的方式去传递这些信息:我们需要一个参数表示消息类型,同时需要处理不同参数类型带来的不同参数列表。一种可能的方式是我们使用一个结构体来将类型和数据包装在一起,同时使用联合体存储数据,可能形如下列的形式:

struct Message{
    MessageType type;
    MessageDate data; // MessageDate 需要是一个Union类型,因为data的数据类型并不确定
}

而这个过程在Rust中会非常简单,只需要为每个消息类型附加一个数据成员即可:

enum Message {
    Power(bool),
    ChangeBrightness(i32),
    ChangeSize(i32,i32),
    Display(String)
}

在这个枚举类型中,我们定义了一系列不同的消息类型,同时为每个类型绑定一个特定类型的数据成员。在发送消息时,只需要生成对应的消息即可:

let bright = 80;
let msg = Message::ChangeBrightness(bright);

SendMessage(msg);

对于接收方,也只需要使用match匹配就可以轻松的从枚举中取出对应的数据成员:

    match msg {
        Message::ChangeBrightness(x) => {
            println!("Target Brightness is {}", x);
            //Do Something
        }
        //....
    };

值得一提的是,上述代码中的x对于命名并无要求,仅仅是作为形式参数;如果你的业务代码对枚举附带的数据成员并不感兴趣,可以直接使用 “_” 通配符替代参数:

match msg {
    //忽略了Message中的参数
    Message::Power(_) => {
        //Do Something
    }
    //忽略了Message中第二个参数,只接收第一个参数
    Message::ChangeSize(x, _) => {
        //Do Something
    } //....
};

枚举高级用法:由数据成员到枚举类的映射

        如果在声明一个带有数据成员的类型的枚举变量时,并不指定这个枚举具体数据成员的值,那么这个变量将会成为一个可调用函数(类似于闭包\Lambda),从它的数据成员类型映射到枚举类型;这种变量可以用于传参,并转化为对应的闭包函数类型;

enum Message {
    I32Data(i32),
}

fn main() {
    //msg 是一个Message枚举类型变量
    let msg1 = Message::I32Data(50);
    
    // f 是一个可调用对象,从i32映射到DemoMessage,相当于 Fn(i32)->DemoMessage
    let f  = Message::I32Data;
    
    //以下的 msg 与 msg1 完全等价
    let msg2 = f(50);
    
    let msg3 = message_i32(50);
}

fn message_i32 (x : i32) -> Message
{
    return Message:I32Data(x);
}

        在上述的示例中,msg1是一个实际的枚举类型变量;注意接下来的 f 变量:它只定义了Message的类型,但是并没有指定具体的值;在这种情况下,f将会成为一个可调用对象,与下文中定义的 message_i32() 方法等价,可以通过传入参数返回对应的枚举类型变量。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值