聊一聊Rust的enum
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的更多相关文章
- Rust入坑指南:亡羊补牢
如果你已经开始学习Rust,相信你已经体会过Rust编译器的强大.它可以帮助你避免程序中的大部分错误,但是编译器也不是万能的,如果程序写的不恰当,还是会发生错误,让程序崩溃.所以今天我们就来聊一聊Ru ...
- Rust 阴阳谜题,及纯基于代码的分析与化简
Rust 阴阳谜题,及纯基于代码的分析与化简 雾雨魔法店专栏 https://zhuanlan.zhihu.com/marisa 来源 https://zhuanlan.zhihu.com/p/522 ...
- Enum, Generic and Templates
文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:https://zhaihj.github.io/enum-generic-and-templat ...
- Rust学习之旅(读书笔记):枚举 (Enum)
Rust学习之旅(读书笔记):枚举 (Enum) C 语言的枚举类型很弱,不如后来的语言,也不如之前的语言.在 C 语言里面枚举量就是一个名字,更方便的定义常量.今天读了<The Rust Pr ...
- 聊一聊Java的枚举enum
一. 什么是枚举 枚举是一种数据类型,具有集合的一些特点,可以存放多个元素,但存储对象有限且固定,枚举也有比较常见的使用场景,如我们需要表达性别(男.女),颜色(红.黄.蓝),星期(星期一.星期二.. ...
- Rust入门篇 (1)
Rust入门篇 声明: 本文是在参考 The Rust Programming Language 和 Rust官方教程 中文版 写的. 个人学习用 再PS. 目录这东东果然是必须的... 找个时间生成 ...
- A First Look at Rust Language
文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:http://zhaihj.github.io/a-first-look-at-rust.html ...
- Writing A Threadpool in Rust
文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:https://zhaihj.github.io/writing-a-threadpool-in- ...
- Rust语言学习笔记(7)
模块 // 兄弟模块 mod network { fn connect() { } } mod client { fn connect() { } } // 父子模块 mod network { fn ...
- Tokio,Rust异步编程实践之路
缘起 在许多编程语言里,我们都非常乐于去研究在这个语言中所使用的异步网络编程的框架,比如说Python的 Gevent.asyncio,Nginx 和 OpenResty,Go 等,今年年初我开始接触 ...
随机推荐
- 一台服务器上部署 Redis 伪集群
哈喽大家好,我是咸鱼 今天这篇文章介绍如何在一台服务器(以 CentOS 7.9 为例)上通过 redis-trib.rb 工具搭建 Redis cluster (三主三从) redis-trib.r ...
- CodeForces 1408D Searchlights
题意 在二维平面有\(n\)个海盗,\(m\)个探照灯,你有两种操作 将所有海盗往上走一步 将所有海盗往右走一步 设海盗为\((a_i,b_i)\),探照灯为\((c_j,d_j)\),当且仅当\(a ...
- Codeforces Round #576 (Div. 2)
A - City Day 题意:给n,x,y和数组a[n],求最小的下标d,使得有a[d-x,d-x+1,--d-1,d+1,d-1,d+1,--d+y-1,d+y]都比a[d]小,若d-x<= ...
- MQ系列14:MQ如何做到消息延时处理
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...
- Verilog实现定点乘法器
实验目的 理解定点乘法的不同实现算法的原理,掌握基本实现算法. 熟悉并运用 Verilog 语言进行电路设计. 为后续设计 CPU 的实验打下基础. 实验内容 定点乘法器有多种实现,实验要求实现迭代乘 ...
- SQL Server查询数据库中的表
SQL Server查询数据库中的表 SSMS中用不了MySQL中的show 查询当前数据库中所有表名: SELECT name FROM sysobjects WHERE (xtype = 'U') ...
- 主动写入流对@ResponseBody注解的影响
问题回溯 2023年Q2某日运营反馈一个问题,商品系统商家中心某批量工具模板无法下载,导致功能无法使用(因为模板是动态变化的) 商家中心报错(JSON串): {"code":-1, ...
- 「codeforces - 1633F」Perfect Matching
link. 首先所有的 activated nodes 组合成了一棵以 \(1\) 为根的有根树.询问即求由 activated nodes 组成的树的最大匹配.对于树上最大匹配有一个贪心策略:自底向 ...
- CFS-GA 相关性特征选择与遗传算法 特征选择/特征提取
CFS-GA特征选择/特征提取 CFS 对于一个样本空间,构造一个二维矩阵A代表此样本空间,A中每行代表一条数据,每列代表一个特征 样本中的数据分为数个特征,其中\(A_i\)表示第\(i\)个特征, ...
- Mybatiplus通用3.5.1版本及其以上的代码生成器工具类
Mybatiplus通用3.5.1版本及其以上的代码生成器工具类 package com.gton.util; import com.baomidou.mybatisplus.annotation.F ...