enum在实际编程中是非常常用的,enum的目的就是为了清晰定义出散落在系统各个角落的相同概念的有限固定值。

一、enum介绍
如果是简单定义固定值,我们可以使用常量const。比如

public const int MAX_THREAD_COUNT=100;

在C语言中,我们可以这样定义一个枚举方便各处使用,比如:

enum Direction
{
Left,
Center,
Right
}

C#基本继承了C的enum性质,简单无别的,比如:

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

当然可以加点其它,比起C要好一丢丢,然而也仅限于此。以至于当这种简单类型无法满足我们需要要扩展的时候就会使用class/struct来取代写出类似这种代码

public sealed class Error
{
public static readonly SignError=new Error(110,"sign error.");
public static readonly NetworkError=new Error(500,"network is disconnected."); public int code{get;private set;}
public string message{get;private set;} public Error(int code,string message)
{
this.code=code;
this.message=message;
}
}

这也是C#的enum鸡肋的地方。当然这并不是枚举了,只不过到达了相似效果。

接着我们来看Java的enum,就会发现它比较好一些了。还拿上面这个例子来说,比如:

public enum Error
{
SignError(110,"sign error."),NetworkError(500,"network is disconnected."); private int code;
private String message; private Error(int code,String message)
{
this.code=code;
this.message=message;
}
}

这么来看C#的变通enum和Java的原生enum能满足我们大多数的使用场景。

在rust中我们也可以声明类C这样的enum,比如:

pub enum GameState{
Wait,Running,Stop,Reboot
}

rust的enum功效不止于此,我们来看看rust的enum的奇特之处。

二、变体enum(可以当有限泛型用-个人理解)
我们可以把不同数据类型放进一个enum里,比如:

pub enum DbParameterValue<'a> {
Null,
I8(i8),
U8(u8),
I16(i16),
U16(u16),
I32(i32),
U32(u32),
I64(i64),
U64(u64),
F32(f32),
F64(f64),
String(&'a str),
StringArray(&'a [&'a str]),
U8Array(&'a [u8]),
}
pub DbParameter<'a>{
pub name:&'a str,
pub value:&'a DbParameterValue<'a>
}
pub DbSql<'a>{
pub db_parameters: Vec<DbParameter<'a>>,
...
}

  可以看到rust的enum可以支持不同类型,以此到达泛型的效果,我个人把这个称作是有限泛型。
  如果是C#或者Java要实现这个,就只能转换成object,这样必然触发拆箱装箱操作。

三、与match完美配合

let value=DbParameterValue::U8(100);
match value{
DbParameterValue::U8(v)=>println!("{v}"),
...
}

  rust的解构操作可以直接取到实际的值,是不是很优雅。

  其实整个rust体系有两个非常重要的enum:Result和Option。除了所有权和借用规则外,这两个也是保障rust安全的法宝。我们来看看它的定义

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

  Ok(T)表示成功,并且包含返回值, T表示正确的返回值的类型(T为泛型);Err(E)表示失败,并且包含了返回值,E表示错误的返回值的类型(泛型)。

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

  Some(T)表示元组结构体,封装了一个 T 类型的值,None表示无。
  比如下面这个函数:

pub fn divide(a:i32,b:i32)->Result<i32,std::io::Error>{
if b==0{
Err(std::io::Error::new(std::io::ErrorKind::Other, "the divisor must not be ( zero )."))
} else{
Ok(a/b)
}
} pub fn try_divide(a:i32,b:i32)->Option<i32>{
if b==0{
None
} else{
Some(a/b)
}
} fn main(){
if let try_value=try_divide(20,100){
println!("try_value is {try_value}");
} let value=divide(25,0);
match value{
Ok(value)=>println!("{value}"),
Err(e)=>panic!("value is {:?}", error)
}
}

  从调用可以看出,我们在实际函数调用的时候经常都必须针对result和option进行处理,这也就是为什么rust没有空指针的原因,所以说rust从根上是内存安全的。当然社区针对result有更好的解决方式(?操作符配合anyhow)。网上很多例子会unwrap();但是请不要这样做,我们要保持漂亮的代码就应该尽量不使用unwrap,并且标准库中也基本使用?操作符。

四、enum的内存布局

看起来enum这么好,有没有缺点呢,缺点当然是有的,我们写个例子来看看

use std::mem::{size_of,size_of_val};

enum PValue<'a>{
U8(u8),
U64(u64),
String(&'a str),
}
fn main(){
println!("size of PValue:{}",size_of::<PValue>());
println!("size of PValue::u8:{}",size_of_val(&PValue::U8(100)));
println!("size of PValue::u64:{}",size_of_val(&PValue::U64(100)));
println!("size of PValue::str:{}",size_of_val(&PValue::String("hello rust,here's my first rust")));
}

  输出结果

size of PValue:24
size of PValue::u8:24
size of PValue::u64:24
size of PValue::str:24

  

可以看出来,enum的内存大小是以其中最大项的内存来分配的。enum每一项都会有1byte的tag分配,当然rust编译器也有特殊优化,比如针对Option<T>就做了优化,舍弃了1byte的tag分配。

One more thing

很久没写文章了,都有点生疏了,以后还是要多练练,我发现后台插入代码居然没有rust语言可选,希望越来越好吧。

etermparser一个个人开源项目,属于个人兴趣。
https://github.com/bmrxntfj/eterm-parser
由于工作需要解析航信eterm系统返回的数据,目前部分实现了av,detr,fd,ml,pat,pnr等指令的结果解析。
有兴趣可以聊聊交个朋友^_^。

聊一聊Rust的enum的更多相关文章

  1. Rust入坑指南:亡羊补牢

    如果你已经开始学习Rust,相信你已经体会过Rust编译器的强大.它可以帮助你避免程序中的大部分错误,但是编译器也不是万能的,如果程序写的不恰当,还是会发生错误,让程序崩溃.所以今天我们就来聊一聊Ru ...

  2. Rust 阴阳谜题,及纯基于代码的分析与化简

    Rust 阴阳谜题,及纯基于代码的分析与化简 雾雨魔法店专栏 https://zhuanlan.zhihu.com/marisa 来源 https://zhuanlan.zhihu.com/p/522 ...

  3. Enum, Generic and Templates

    文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:https://zhaihj.github.io/enum-generic-and-templat ...

  4. Rust学习之旅(读书笔记):枚举 (Enum)

    Rust学习之旅(读书笔记):枚举 (Enum) C 语言的枚举类型很弱,不如后来的语言,也不如之前的语言.在 C 语言里面枚举量就是一个名字,更方便的定义常量.今天读了<The Rust Pr ...

  5. 聊一聊Java的枚举enum

    一. 什么是枚举 枚举是一种数据类型,具有集合的一些特点,可以存放多个元素,但存储对象有限且固定,枚举也有比较常见的使用场景,如我们需要表达性别(男.女),颜色(红.黄.蓝),星期(星期一.星期二.. ...

  6. Rust入门篇 (1)

    Rust入门篇 声明: 本文是在参考 The Rust Programming Language 和 Rust官方教程 中文版 写的. 个人学习用 再PS. 目录这东东果然是必须的... 找个时间生成 ...

  7. A First Look at Rust Language

    文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:http://zhaihj.github.io/a-first-look-at-rust.html ...

  8. Writing A Threadpool in Rust

    文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:https://zhaihj.github.io/writing-a-threadpool-in- ...

  9. Rust语言学习笔记(7)

    模块 // 兄弟模块 mod network { fn connect() { } } mod client { fn connect() { } } // 父子模块 mod network { fn ...

  10. Tokio,Rust异步编程实践之路

    缘起 在许多编程语言里,我们都非常乐于去研究在这个语言中所使用的异步网络编程的框架,比如说Python的 Gevent.asyncio,Nginx 和 OpenResty,Go 等,今年年初我开始接触 ...

随机推荐

  1. 想了解API接口,这一篇就够了

    API(Application Programming Interface)接口,对于大多数人来说可能还比较陌生,但实际上我们每天都在与它打交道.无论是使用手机上的应用程序,还是在网上购物,都少不了A ...

  2. mysql 大表如何ddl 👑

    大家好,我是蓝胖子,mysql对大表(千万级数据)的ddl语句,在生产上执行时一定要千万小心,一不小心就有可能造成业务阻塞,数据库io和cpu飙高的情况.今天我们就来看看如何针对大表执行ddl语句. ...

  3. PGO in Go 1.21

    原文在这里. 由 Michael Pratt 发布于 2023年9月5日 在2023年早些时候,Go 1.20发布了供用户测试的概要版本的基于性能分析的优化(PGO).经过解决预览版已知的限制,并得益 ...

  4. 5-MySQL列定义

    1.列定义 说明:在MySQL中,列定义(Column Definition)是用于定义数据库表中每一列的结构的语句.它指定了列的名称.数据类型.长度.约束以及其他属性. 2.主键和自增 主键:PRI ...

  5. flask中cookies的使用

    flask中cookies的使用 在Flask中对cookie的处理 1. 设置cookie: 设置cookie,默认有效期是临时cookie,浏览器关闭就失效 可以通过 max_age 设置有效期, ...

  6. Redis系列内容完整版

    @ 目录 Redis系列之_Redis介绍安装配置 第一章 redis初识 1.1 Redis是什么 1.2 Redis特性(8个) 1.3 Redis单机安装 1.3.1下载安装 1.3.2三种启动 ...

  7. buu pwn wp(持续更新)

    1.warmup_csaw_2016 main函数如下 __int64 __fastcall main(int a1, char **a2, char **a3) { char s[64]; // [ ...

  8. Godot引擎的一些踩坑记录(不断更新中)

    版本号 Godot 3.1.2 文件夹名称使用小写.编译\导出时有的tscn文件的引用路径, 有可能会变成小写路径(怀疑是bug),导致启动失败. ttc字体(文泉驿微米黑)导出时需要手动设置包含*. ...

  9. LNOI 2023 游记

    Day -1 持续性的精神状态不太好,分明睡觉起床时间都没变,但白天就是非常非常困,为什么呢. 补不动任何题,脑子完全不转...... Day 0 13:30 才被家长叫醒,四点左右到了开发区还是好困 ...

  10. go 中如何实现定时任务

    定时任务简介 定时任务是指按照预定的时间间隔或特定时间点自动执行的计划任务或操作.这些任务通常用于自动化重复性的工作,以减轻人工操作的负担,提高效率.在计算机编程和应用程序开发中,定时任务是一种常见的 ...