rust学习七、枚举
枚举是十分特别的类型,是因为和大部分语言中看起来不太一样。
最大的不同之一在于:允许每个成员具有不同的类型的属性
注:本文内容根据<<The rust programmming Language>>有关章节编写
如果没有特别说明,"rust学习"系列基本上都是根据此书编写,不再在后续的篇幅中说明
一、定义、赋值和打印
结论:
示例
#[derive(Debug)] enum simple_enum { Man, Women, Other } //b. 枚举类型中包含数据 #[derive(Debug)] enum color { Red, Green = 3 } //c.枚举类型中包含多个数据,且都是一样的 #[derive(Debug)] #[derive(PartialEq)] enum grade { A(u32,String), B(u32,String), C(u32,String) } //枚举的方法,定义上了struct方法类似,都是impl,&self impl grade { fn print(&self) { //这个tmd也太麻烦了吧 match self { grade::A(a,b) => println!("{}-{}",a,b), grade::B(a,b) => println!("{}-{}",a,b), grade::C(a,b) => println!("{}-{}",a,b) } } } #[derive(Debug)] enum food<'a>{ 蔬菜([String;5]), 禽肉, 猪肉, 牛羊肉, 海鲜, 大米, 水果(&'a str,&'a str,&'a str) } fn main() { let a = simple_enum::Man; println!("{:?}",a); // 使用println! :?宏打印枚举的时候,变量名称不能同枚举成员。否则会报错 let gr = color::Green; println!("{:?}",gr); let r = color::Red; println!("{:?}",r); let grade_a = grade::A(10,String::from("差")); let grade_a1=grade::A(9,String::from("差011")); grade_a.print(); println!("{:?}",grade_a); if (grade_a == grade_a1){ println!("相等"); } else{ println!("不相等"); } let fruit= food::水果("苹果","香蕉","梨子"); println!("{:?}",fruit); print_first_fruit_member(fruit); let v = food::蔬菜(["白菜".to_string(),"萝卜".to_string(),"黄瓜".to_string(),"冬瓜".to_string(),"南瓜".to_string()]); println!("{:?}",v); } fn print_first_fruit_member<'a>(fruit: food<'a>) { if let food::水果(first, second, third) = fruit { println!("{},{},{}", first,second,third); } else { println!("The provided food is not a fruit."); } }
上例中,food允许不同成员有不同的类型值。
二、特别的枚举-Option
Option<T>{
Some(T),
None
}
特殊点在于:
1.定义于标准库中
2.它甚至被包含在了 prelude 之中,你不需要将其显式引入作用域
3.它的成员也是如此,可以不需要 Option:: 前缀来直接使用 Some 和 None
有用是怎么体现出来的?
1.首先Option具有许多方法,比如is_some()、is_none()、unwrap()等
2.Option<T>可以作为函数参数,比如fn some_function(option: Option<i32>)
以下是重点(基本上摘抄的):
那么当有一个 Option<T> 的值时,如何从 Some 成员中取出 T 的值来使用它呢?
Option<T> 枚举拥有大量用于各种情况的方法:
你可以查看它的文档。熟悉 Option<T> 的方法将对你的 Rust 之旅非常有用(原文)
fn main() { let some_number = Some(5); // some_number的类型是Option<i32> let some_string = Some("a string"); // some_string的类型是Option<&str> let absent_number: Option<i32> = None; // absent_number的类型是Option<i32> println!("some_number is {:?}", some_number); println!("some_string is {:?}", some_string); println!("absent_number is {:?}", absent_number); println!("absent_number is {:?}", absent_number.is_none()); let name:String; name.push_str("abc"); }
非常有用? 还没有体会出来,先记着!
三、match和枚举
这里主要讨论如何匹配枚举的值,rust的解决方案是match。
1.match某种程度上可以看作rust中if-else的升级版,只不过match有时候可以让我们少打印一些代码而已
match不像java的switch那么傻瓜(传统),需要在每个分支上break,否则会继续执行下一个分支,虽然有那种情况存在,但是绝大部分时候,
我们并不需要break。
rust的match和新版本的java switch写法更加一致,已经不需要break了,且不会傻乎乎地每个匹配过去
2.顺便说一下,rust的创始人很喜欢下划线。所以下划分可以分割变量,也可以单独作为一个变量,但是不建议这么做
3.关于分支值的匹配
a.rust的分支匹配是穷尽的,意思就是编译器如果发现你存在一些未匹配的分支,就会报错。 我们记住这个就可以了,因为编译器很贴心第处理了这个。
这一点,的确比许多编译器好多了。
b.rust可以使用多种方式匹配某个分支值,包括单个值、多个值、所有值
c.rust使用下划线_来表示所有值,这样我们就可以直接使用这个变量了。这个倒是比许多语言的default稍微省事一些。这个并不是重点
d._和other都可以表示所有值,能一起用,不会编译错误,但是固定有一个分支不可达到。
这意味着,rust的编译器目前虽然很贴心,但也不会浪费过多的时间去判断是否具有相同的分支--因为它允许有相同的分支,但是
它的匹配机制使得只会有其中一个分支被执行,其它相同的并会被处理
e.当某个分支值满足之后,匹配代码就结束了,不会像传统的java switch那么傻叉继续执行后面的。这个符合大部分人的思维
示例
/** * 年代枚举 */ #[derive(Debug)] enum Age{Year1970,OtherYear} /** * 硬币枚举 */ enum Coin{一分(Age),二分,五分,一角,贰角,五角,一元} /** * 文明枚举 */ #[derive(Debug)] enum Civil{中国(Age),欧洲,美国,非洲} fn main(){ let coin = Coin::五分; let one =Coin::一元; match_branch_with_multiple_method(coin); match_branch_with_multiple_method(one); let civil = Civil::中国(Age::Year1970); let _ = Civil::欧洲; //奇特的无意义的变量名称_,可以用,但是不建议。因为你无法直接传递,转赋也不方便,也不利于维护 //let bad_=_; //这样会报错 //match_and_use_branch_value(_) //这样会报错 let _欧洲 = Civil::欧洲; match_and_use_branch_value(civil); match_and_use_branch_value(_欧洲); } fn match_branch_with_multiple_method(coin:Coin){ //演示如何匹配多值分支的方式:单个值、多个值、所有值 match coin{ Coin::一分(Age::Year1970) => println!("一分"), //匹配分支特定值 Coin::一分(_) => println!("一分"), //匹配分支所有值 Coin::一分(Age::OtherYear) | Coin::一分(Age::Year1970) => println!("一分"), //匹配分支多个值 Coin::二分 => println!("二分"), Coin::五分 => println!("五分"), Coin::一角 => println!("一角"), Coin::五角 => println!("五角"), other => println!("一元 of other"), _=> println!("一元") }; } fn match_and_use_branch_value(civil:Civil){ //这种方式,可以利用类似匹配元组元素的方式,直接捕获分支值,并使用 match civil { Civil::中国(age) => println!("有小聪明的中国:{:?},偶尔有一些亮色能拯救他们",age), //自动从civil中捕获age值 Civil::欧洲 => println!("肮胀但又聪明的欧洲"), _ => println!("{:?}",civil) //如果把这个匹配去掉,那么会引发编译错误: patterns `Civil::美国` and `Civil::非洲` not covered }; }
如上例,match对枚举的匹配是穷尽的,这意味着一个值没有匹配,编译器就报错,这对于工程师倒是挺友好的!
四、语法糖if let
示例
#[derive(Debug)] enum Age{Year1970,OtherYear} /** * 硬币枚举 */ enum Coin{一分(Age),二分,五分,一角,贰角,五角,一元} fn main(){ let coin = Coin::一分(Age::Year1970); match coin{ Coin::一分(Age::Year1970) => println!("70特有硬币?"), _=>println!("不是一分硬币") } //rust 的语法糖 if let Coin::一分(Age::Year1970) = coin{ println!("70特有硬币?"); }else{ println!("不是一分硬币"); } //不带 else的if let if let Coin::一分(Age::OtherYear)=coin{ println!("不是70特有硬币"); } }
语法糖这个东西没有什么可说的,就是图方便而已。
不过这个和标准的if {} 又有什么区别了?
如果没有ide和代码助手,那么的确还是不错的! 但你如果有ide和代码助手,那么这些语法糖的主要作用就是困扰。
五、小结
总而言之,rust的枚举和大部分语言看起来并没有什么本质的上的区别,但的确又有一些区别。
rust发明人的宗旨:让语言尽量用起来方便一些。 看起来好像得到了体现。 就我个人而言,不是太苟同!
rust的编译器的大作用,在这里有了体现!