30天拿下Rust之枚举

概述

Rust中的枚举是一种用户定义的类型,它允许你为一组相关的值赋予友好的名称。在Rust中,枚举是强大的工具,它们不仅仅用于表示几个固定的值,还可以包含函数和方法,使得枚举成员可以有自己的行为。通过与模式匹配和其他Rust特性结合使用,枚举在构建健壮、无崩溃的应用程序中发挥了重要作用,并可大幅提高代码的可读性、可维护性和类型安全性。

基础枚举

在Rust中,枚举通过关键字enum进行声明,它可以包含一组相关的命名常量。比如:我们可以定义一个枚举来表示一周的几天。

enum Day {

    Monday,

    Tuesday,

    Wednesday,

    Thursday,

    Friday,

    Saturday,

    Sunday,

}

定义好枚举后,我们可以像下面这样使用枚举值。

let cur_day = Day::Wednesday;

关联枚举

Rust中的枚举还可以带有关联值,这使得枚举成员可以有不同的数据类型。比如:我们可以定义一个表示结果的枚举,其中一个成员包含整数值,另一个成员则包含字符串值。

enum Result {

    Ok(i32),

    Err(String),

}

fn main() {

    let success = Result::Ok(66);

    let failure = Result::Err(String::from("failed"));

}

在上面的示例代码中,Result::Ok有一个i32类型的关联值,Result::Err有一个String类型的关联值。

另外,我们还可以为枚举中的属性命名,类似于结构体的语法。但请特别注意:枚举并不能像访问结构体字段那样访问枚举绑定的属性,访问的方法可参考下面的匹配枚举。

enum Shape {

    Point {x: i32, y: i32},

    Rectangle {width: i32, height: i32},

    Circle(i32),

}

fn main() {

    let point = Shape::Point{x: 66, y: 88};

    let rect = Shape::Rectangle{width: 10, height: 20};

    let circle = Shape::Circle(100);

}

匹配枚举

使用match表达式,可以很方便地处理枚举类型的值。Rust强制要求枚举的所有可能变体在match表达式中都被考虑到,以避免未处理的枚举导致的运行时错误。

enum Direction {

    Up(u32),

    Down(i32),

    Left(String),

    Right(String),

}

fn main() {

    let direction = Direction::Up(66);

    match direction {

        Direction::Up(value) => println!("turn up by {}", value),

        Direction::Down(value) => println!("turn down by {}", value),

        Direction::Left(text) => println!("turn left to {}", text),

        Direction::Right(text) => println!("turn right to {}", text),

    }

}

match表达式也可以当作函数表达式来对待,它是可以有返回值的。但有一点需要注意:所有返回值表达式的类型必须一样。

enum Direction {

    Up(u32),

    Down(i32),

    Left(String),

    Right(String),

}

fn convert(direction: Direction) -> u32 {

    match direction {

        Direction::Up(value) => 100,

        Direction::Down(value) => 200,

        Direction::Left(text) => 300,

        Direction::Right(text) => 400,

    }

}

fn main() {

    let value = convert(Direction::Down(99));

    println!("{}", value);

}

在match表达式中,还可以使用通配符,用于对一些特定的值采取特殊操作,而对其他的值采取默认操作。在下面的示例代码中,我们对88和99采取了特殊操作,但对其他值采取了统一的默认处理。

fn main() {

    let value = 66;

    match value {

        88 => println!("conditon 88"),

        99 => println!("conditon 99"),

        other => println!("other conditon {}", other),

    }

}

注意:我们必须将通配分支放在最后,因为模式是按顺序匹配的。如果我们在通配分支后再添加其他分支,Rust在编译时会警告我们“unreachable pattern”,因为此后的分支永远不会被匹配到。

另外,Rust还提供了一种模式:当我们不想使用通配模式获取的值时,可以使用占位符_。这是一种特殊的模式,可以匹配任意值而不绑定到该值。占位符会告诉Rust,我们不会使用这个值,因此Rust也不会警告我们存在未使用的变量。

fn main() {

    let value = 66;

    match value {

        88 => println!("conditon 88"),

        99 => println!("conditon 99"),

        _ => println!("other conditons"),

    }

}

可以看到,对于只有两种匹配情况的场景来说,match显得比较繁琐,必须使用通配符或占位符。为此,Rust提供了语法糖if let,用于简化代码。可以在if let中包含一个else,else块中的代码与match表达式中占位符分支块中的代码相同。

fn main() {

    let value = 66;

    if let 66 = value {

        println!("conditon 66");

    } else {

        println!("other conditons");

    }

}

使用if let,意味着编写更少的代码,但这会失去match强制要求的穷尽性检查(因为else是可选的)。到底该使用match还是if let,取决于我们对增加简洁度和失去穷尽性检查之间的权衡取舍。

Option枚举

Rust中的Option类型是一种枚举,它是Rust语言的核心特性之一,用于处理值可能存在的状态。在许多其他编程语言中,这种场景可能会使用null、None或其他表示空或缺失的特殊值来处理,但这些通常会导致潜在的空引用错误。而Rust通过设计Option<T>类型,强制开发者在编译时就必须考虑值可能不存在的情况,从而保证了运行时的安全性。

Option类型的定义如下:

pub enum Option<T> {

    None,

    Some(T),

}

Option<T>中,T是一个泛型参数,代表了当值存在时的具体类型。这意味着,Option可以包裹任何类型的值。比如:Option<i32>表示可能包含一个整数值,或者没有值。

Option类型提供的主要方法如下。

unwrap(): 如果Option是Some(value),则返回该value;如果Option是None,则触发异常。这主要用于开发阶段调试和确定程序逻辑正确的地方,不推荐在生产代码中滥用,因为它会直接终止程序执行。

expect(msg): 类似于unwrap(), 但在触发异常时,提供了一个自定义的消息。

is_none(): 返回一个布尔值,表示Option是否为None。

is_some(): 返回一个布尔值,表示Option是否有值。

ok_or(err): 将Option转换成Result,若为Some则映射到Ok(_),若为None则映射到Err(err)。

map(f): 如果Option是Some(T),应用函数f给T并返回一个新的Option(T'是f作用后的新类型)。如果Option是None,则返回None。

and_then(f): 类似于map(),但是f必须返回一个Option,它将链式调用并保持Option的状态。

unwrap_or(default): 如果Option是Some,则返回其中的值。否则,返回提供的默认值。

unwrap_or_else(f): 类似于unwrap_or(),但当Option为None时,调用函数f生成默认值。

Option类型的具体使用方法,可参考下面的示例代码。

fn divide(a: i32, b: i32) -> Option<i32> {

    if b == 0 {

        None

    } else {

        Some(a / b)

    }

}

fn main() {

    let result = divide(10, 2);

    match result {

        Some(value) => println!("result is: {}", value),

        None => println!("can't be zero"),

    }

    // 当除数非零时,得到结果;否则,返回-1。

    let value = divide(10, 0).unwrap_or(-1);

    println!("value is: {}", value);

    // 使用map进行链式操作,输出: Some(10)

    let number = Some(5);

    let number2 = number.map(|n| n * 2);

    println!("number2 is: {:?}", number2);

}

Option类型在Rust中有着广泛的应用场景,可以用于初始化值、作为函数的返回值、表示简单错误、作为结构体的可选字段等。通过使用Option,我们可以更加明确地处理可能为空的情况,从而避免许多由于空值引起的运行时错误。这也是Rust语言相对于C/C++等语言的一大明显优势。

枚举绑定方法

与结构体类似,Rust的枚举还允许你在枚举成员上定义函数和方法。比如:我们可以给上面的Result枚举添加一个describe方法。

enum Result {

    Ok(i32),

    Err(String),

}

impl Result {

    fn describe(&self) -> &str {

        match self {

            Result::Ok(_) => "Operation was successful",

            Result::Err(_) => "Operation failed",

        }

    }

}

fn main() {

    let success = Result::Ok(42);

    let failure = Result::Err(String::from("something went wrong"));

    println!("{}", success.describe());

    println!("{}", failure.describe());

}

总结

Rust的枚举提供了一种安全且灵活的方式来处理多种可能的状态和值,使用枚举的优点主要有以下几点。

1、代码清晰性:使用枚举可以使代码更具可读性和可维护性,因为它们为可能的值提供了明确的名称。

2、类型安全:枚举是强类型的,这意味着不能将错误的类型分配给枚举值。

3、灵活性:枚举可以包含关联值,这使得它们能够表示更复杂的数据结构。

4、扩展性:可以在任何时候向枚举添加新的成员,而不会破坏现有的代码。

总之,理解和熟练运用枚举,能够使我们在Rust编程过程中设计出更为简洁、优雅的代码结构。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容

  • 概述 Rust作为一种系统编程语言,近年来在开发者社区中逐渐崭露头角。其独特的所有权系统和内存安全特性,使得Rus...
    希望_睿智阅读 403评论 -1 2
  • 变量 声明变量关键字:let 变量值分为两种类型: 可变的(mut) 不可变 变量类型: 布尔型 - bool 表...
    JennerHua阅读 906评论 0 4
  • 通用编程概念 变量与可变性 变量默认不可变,如需要改变,可在变量名前加 mut 使其可变。例如:let mut a...
    soojade阅读 12,571评论 2 30
  • 本文将汇总Rust 1.0之后各版本在语法和部分库函数方面的更新,略过编译器和标准库的性能优化、工具链和文档的改进...
    人间正道是数学阅读 2,403评论 0 2
  • 概述 在Rust中,函数是程序的基本构建块,是代码复用的基本单位,用于封装一系列执行特定操作和计算的语句。函数是可...
    希望_睿智阅读 127评论 0 2