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. 零代码,使用 Dify 和 Laf 两分钟接入企业微信 AI 机器人

    Dify 允许创建 AI 应用,并提供二次开发的能力.这里我将演示创建一个法律问答助手的 AI 应用,称作"知法".在本篇教程中,我将指导你为"知法"接入企业微 ...

  2. SqlServer修改表字段类型

    if not exists (select 1 from syscolumns where name='字段名' and id=OBJECT_ID('表名') and 条件) begin alter ...

  3. NebulaGraph实战:3-信息抽取构建知识图谱

      自动信息抽取发展了几十年,虽然模型很多,但是泛化能力很难用满意来形容,直到LLM的诞生.虽然最终信息抽取质量部分还是需要专家审核,但是已经极大的提高了信息抽取的效率.因为传统方法需要大量时间来完成 ...

  4. .NET应用如何防止被反编译

    前言 前段时间分享了两篇关于.NET反编译相关的文章,然后文章留言区就有小伙伴提问:如何防止被反编译?因此本篇文章我们就来讲讲.NET应用如何防止被反编译..NET反编译相关的文章可以看如下文章: 4 ...

  5. 【算法】游戏中的学习,使用c#面向对象特性控制游戏角色移动

    最近,小悦的生活像是一首繁忙的交响曲,每天忙得团团转,虽然她的日程安排得满满当当,但她并未感到充实.相反,她很少有时间陪伴家人,这让她感到有些遗憾.在周五的午后,小悦的哥哥突然打来电话,他的声音里充满 ...

  6. 深入理解 Python 虚拟机:协程初探——不过是生成器而已

    深入理解 Python 虚拟机:协程初探--不过是生成器而已 在 Python 3.4 Python 引入了一个非常有用的特性--协程,在后续的 Python 版本当中不断的进行优化和改进,引入了新的 ...

  7. Sell Pigs 题解

    Sell Pigs 双倍经验 题目大意 有 \(n\) 个顾客前来买猪,共有 \(m\) 个猪圈,每个顾客携带着某一些猪圈的钥匙,需要买一定数量的猪.在顾客买完后,我们可以将打开的猪圈中的猪随意移动, ...

  8. 虹科分享 | HPC调度解决方案:HK-Adaptive在数字卫星图像领域的应用

    2011年3月11日,日本海岸附近发生了9.0级地震.这次地震引发了强大的海啸,并向内陆传播了6英里,不仅使地球的轴心偏移了大约10到25厘米,还导致福岛核电站发生核紧急情况. 为了减少这场灾害的损失 ...

  9. docker常用命令-docker网络

    docker命令详解 docker search 在docker hub中搜索镜像: docker pull 从docker镜像源服务器拉取指定镜像或者库镜像: docker push 推送指定镜像或 ...

  10. Python 日期和时间处理教程:datetime 模块的使用

    Python 中的日期不是独立的数据类型,但我们可以导入一个名为 datetime 的模块来使用日期作为日期对象. 示例:导入 datetime 模块并显示当前日期: import datetime ...