Rust学习笔记(五十四)模式(匹配)语法
匹配字面值
模式匹配可以直接匹配字面值,例:fnmain(){letx1;matchx{1println!(one),2println!(two),3println!(three),println!(anything),}}匹配命名变量
命名变量是可匹配任何值的不可辩驳模式,例:fnmain(){letxSome(5);lety10;matchx{Some(50)println!(Got50),Some(y)println!(Matched,y{:?},y),Some(y)匹配的是x,即Some(5),所以此处y是5println!(Defaultcase,x{:?},x),}println!(attheend:x{:?},y{:?},x,y);}
运行输出:Matched,y5attheend:xSome(5),y10
因为模式匹配里的y在模式匹配后已走出作用域,后面打印的y是外面的y。多重匹配
在match表达式中,使用(或)语法,可以匹配多种模式,例:fnmain(){letx1;matchx{12println!(oneortwo),3println!(three),println!(anything),}letxSome(5);matchx{Some(125)println!(here),println!(lalala),}}使用。。来匹配某个范围的值
例:fnmain(){letx5;matchx{1。。5println!(onethroughfive),println!(somethingelse),}letxc;matchx{a。。jprintln!(earlyASCIIletter),k。。zprintln!(lateASCIIletter),println!(somethingelse),}}解构以分解值
可以使用模式来解构结构体、枚举、元组,从而引用这些类型值的不同部分。例:structPoint{x:i32,y:i32,}fnmain(){letpPoint{x:0,y:7};letPoint{x:a,y:b}p;asserteq!(0,a);asserteq!(7,b);letPoint{x,y}p;asserteq!(0,x);asserteq!(7,y);matchp{Point{x,y:0}println!(Onthexaxisat{},x),匹配x随意,y是0的情况Point{x:0,y}println!(Ontheyaxisat{},y),匹配y随意,x是0的情况Point{x,y}println!(Onneitheraxis:({},{}),x,y),匹配x随意,y也随意的情况}}解构枚举
例:enumMessage{Quit,Move{x:i32,y:i32},结构体枚举变体Write(String),ChangeColor(i32,i32,i32),}fnmain(){letmsgMessage::ChangeColor(0,160,255);matchmsg{Message::Quit{println!(TheQuitvarianthasnodatatodestructure。)}Message::Move{x,y}{println!(Moveinthexdirection{}andintheydirection{},x,y);}Message::Write(text)println!(Textmessage:{},text),Message::ChangeColor(r,g,b){println!(Changethecolortored{},green{},andblue{},r,g,b)}}}解构嵌套的结构体和枚举
例:enumColor{Rgb(i32,i32,i32),Hsv(i32,i32,i32),}enumMessage{Quit,Move{x:i32,y:i32},Write(String),ChangeColor(Color),}fnmain(){letmsgMessage::ChangeColor(Color::Hsv(0,160,255));matchmsg{Message::ChangeColor(Color::Rgb(r,g,b)){println!(Changethecolortored{},green{},andblue{},r,g,b)}Message::ChangeColor(Color::Hsv(h,s,v)){println!(Changethecolortohue{},saturation{},andvalue{},h,s,v)}(),}}解构结构体和枚举
例:structPoint{x:i32,y:i32,}fnmain(){let((feet,inches),Point{x,y})((3,10),Point{x:3,y:10});}在模式中忽略值
有几种方式可以在模式匹配中忽略整个值或部分值:配合其它模式使用以开头的名称。。忽略值的剩余部分fnfoo(:i32,y:i32){忽略整个值println!(Thiscodeonlyusestheyparameter:{},y);}fnmain(){foo(3,4);}
大部分情况当你不再需要特定函数参数时,最好修改签名不再包含无用的参数。在一些情况下忽略函数参数会变得特别有用,比如实现trait时,当你需要特定类型签名但是函数实现并不需要某个参数时。此时编译器就不会警告说存在未使用的函数参数,就跟使用命名参数一样。fnmain(){忽略部分letmutsettingvalueSome(5);letnewsettingvalueSome(10);match(settingvalue,newsettingvalue){(Some(),Some()){只要求settingvalue和newsettingvalue都是Some即可,忽略里面的值println!(Cantoverwriteanexistingcustomizedvalue);}{settingvaluenewsettingvalue;}}println!(settingis{:?},settingvalue);letnumbers(2,4,8,16,32);matchnumbers{(first,,third,,fifth){println!(Somenumbers:{},{},{},first,third,fifth)}}}fnmain(){letx5;开头会忽略未使用的变量lety10;没有,并且未使用的变量会被编译器警告}
注意,只使用和使用以下划线开头的名称有些微妙的不同:比如x仍会将值绑定到变量,而则完全不会绑定。fnmain(){letsSome(String::from(Hello!));ifletSome(s)s{println!(foundastring);}println!({:?},s);编译器报错,因为变量仍然会被绑定值,所以s的值会移动进s,这时s无法使用了。}fnmain(){letsSome(String::from(Hello!));ifletSome()s{只有下划线不会绑定值,所以通过编译println!(foundastring);}println!({:?},s);}fnmain(){structPoint{x:i32,y:i32,z:i32,}letoriginPoint{x:0,y:0,z:0};使用。。忽略x以外的字段matchorigin{Point{x,。。}println!(xis{},x),}}fnmain(){letnumbers(2,4,8,16,32);matchnumbers{忽略中间的部分(无论中间有多少个),只要第一个和最后一个(first,。。,last){println!(Somenumbers:{},{},first,last);}}}fnmain(){letnumbers(2,4,8,16,32);matchnumbers{存在歧义,因为编译器不知道我们要的是中间的哪一个元素,所以无法编译(。。,second,。。){println!(Somenumbers:{},second)},}}使用match守卫来提供额外的条件
match守卫就是match分支后额外的if条件,若想要匹配成功该条件也必须满足。match守卫适应于比单独的模式更复杂的场景,例:fnmain(){letnumSome(4);matchnum{Some(x)ifx5println!(lessthanfive:{},x),Some(x)println!({},x),None(),}letx4;letyfalse;matchx{配合多重匹配456ifyprintln!(yes),println!(no),}}绑定
符号让我们可以创建一个变量,该变量可以在测试某个值是否与模式匹配的同时保存该值。例:fnmain(){enumMessage{Hello{id:i32},}letmsgMessage::Hello{id:5};matchmsg{Message::Hello{id:idvariable3。。7,要求id的值在3到7的范围时,将其值保存在idvariable中}{println!(Foundanidinrange:{},idvariable)}Message::Hello{id:10。。12}{println!(Foundanidinanotherrange)}Message::Hello{id}{println!(Foundsomeotherid:{},id)}}}