面向JavaScript开发人员的Rust-模式匹配和枚举

2020-07-12 23:33:34

这是向JavaScript开发人员介绍Rust语言系列文章的第四部分。以下是过去的章节:

为了理解模式匹配,让我们从JavaScript-switch大小写中熟悉的内容开始。

函数PRINT_COLOR(COLOR){Switch(COLOR){CASE";ROSE";:CONSOLE。原木(#34;玫瑰是红色的,#34;);折断;表壳";紫色";:控制台。日志(";紫罗兰是蓝色的,";);中断;默认:控制台。LOG(";糖是甜的,你也是。";);}}PRINT_COLOR(";ROSE&34;);//玫瑰是红色的,PRINT_COLOR(";紫色&34;);//紫罗兰是蓝色的,PRINT_COLOR(";You";);//糖是甜的,你也是。

fn print_color(color:&;str){Match color{";Rose";=>;println!(";玫瑰是红色的,";),";紫罗兰";=>;println!(";紫罗兰是蓝色的,";),_=>;println!(";糖是甜的,你也是甜的。";糖是甜的,你也是甜的。";糖是甜的,你也是甜的。";糖是甜的,你也是。";糖是甜的,你也是甜的。),}}fn main(){print_color(";Rose";);//玫瑰是红色的,print_color(";紫罗兰";);//紫罗兰是蓝色的,print_color(";you";);//糖是甜的,你也是。}。

大部分代码应该可以立即理解。匹配表达式具有以下签名:

胖箭头=>;语法可能会绊倒我们,因为它与JavaScript箭头函数相似,但它们是无关的。最后一个使用下划线_的模式称为CatchAll模式,类似于切换情况下的默认情况。每个模式=>;表达式组合称为匹配臂。

上面的示例并没有真正传达出模式匹配有多有用-它看起来就像是具有不同语法和花哨名称的switch case。让我们讨论一下解构和枚举,以了解模式匹配为什么有用。

析构是将数组或结构的内部字段提取到单独变量中的过程。如果您在JavaScript中使用过解构,那么它在Rust中非常相似。

设RGB=[96,172,57];设[红,绿,蓝]=RGB;控制台。log(红色);//96console。log(绿色);//172控制台。log(蓝色);//57设Person={name:";shesh";,City:";Singapore";};设{name,City}=Person;Console。log(Name);//nameconsole。log(城市);//城市。

struct Person{name:string,City:String,}fn main(){let rgb=[96,172,57];let[red,green,Blue]=rgb;println!(";{}";,red);//96 println!(";{}";,green);//172 println!(";{}";,Blue);//57设Person=Person{name:";shesh";。to_string(),城市:";新加坡";。to_string(),};let Person{name,City}=Person;println!(";{}";,name);//name println!(";{}";,City);//City}。

编写“如果这个那么那个”类型的代码是非常常见的。通过将解构和模式匹配相结合,我们可以非常简洁地编写这些类型的逻辑。

让我们以下面的JavaScript示例为例。它是人为的,但你可能在你的职业生涯中的某个时候写过这样的东西:

常量point={x:0,y:30};const{x,y}=point;if(x=0&;&;y=0){控制台。log(";两者都是零";);}Else if(x=0){控制台。log(`x为零,y为${y}`);}Else if(y=0){控制台。log(`x为${x},y为零`);}否则{控制台。log(`x为${x},y为${y}`);}。

结构点{x:I32,y:I32,}fn main(){let point=Point{x:10,y:0};匹配点{Point{x:0,y:0}=>;println!(";两者都是零";),点{x:0,y}=>;println!(";x是零,y是{}";,y),点{x,y:0}=>;println!(";x是{},y是零";,x),点{x,y}=>;println!(";x是{},y是{}";,x,y),}}。

与if Else逻辑相比,它有点简洁,但也可能会令人困惑,因为我们同时执行比较、析构和变量绑定。

我们开始明白为什么它被命名为“模式匹配”-我们接受输入,看看火柴臂中的哪个模式“更适合”-它就像孩子们玩的形状分类玩具。除了比较,我们还在第2,3,4场比赛中做可变装订。我们将变量x和/或y传递给它们各自的表达式。

模式匹配也是详尽的-也就是说,它迫使您处理所有可能的情况。尝试删除最后一个匹配的ARM,Rust不会让您编译代码。

JavaScript没有枚举,但是如果您使用过tyescript,您可以认为Rust的枚举是typecript的枚举和typecript的区别联合的组合。

例如,即使JavaScript没有枚举,您也可以使用以下模式:

常量方向={前进:";前进";,向后:";向后";,左:";左";,右:";右";,};函数MOVE_DRONE(方向){开关(方向){案例方向.FORWARD:控制台。记录(";向前移动";);中断;案例方向.BACKWARD:控制台。记录(";向后移动);中断;案例方向。左:控制台。记录(";向左移动);中断;案例方向。右:控制台。日志(";向右移动";);中断;}}MOVE_DRONE(方向.FORWARD);//";向前移动";

在这里,我们可以将向前、向后、向左和向右定义为单独的常量,但是将其分组到Direction对象中有以下好处:

名称向前、向后、向左和向右在方向下进行命名空间,因此可以避免命名冲突。

它是自我文档化的,因为我们可以快速查看代码库中所有可用的有效说明。

如果有人将NORTH或UP作为参数传递给MOVE_DRONE怎么办?要解决此问题,我们可以添加一个验证,以确保在Move函数中只允许Direction对象中存在的值。

如果我们决定在将来支持向上和向下,或者将左/右更名为左舷/右舷,该怎么办?我们需要找出所有使用类似开关盒或IF-ELSE的地方。我们有可能会错过一些会在生产中引起问题的地方。

强类型语言(如Rust)中的枚举功能更强大,因为它们无需我们编写额外代码即可解决这些问题。

如果函数只能接受一小部分有效输入,则可以使用枚举来强制执行此约束。

使用模式匹配枚举会强制您覆盖所有情况。在将来更新枚举时非常有用。

枚举方向{向前、向后、左、右}fn MOVE_DRONE(方向:方向){匹配方向{方向::向前=>;println!(";向前移动";),方向::向后=>;println!(";向后移动),方向::Left=>;println!(";向左移动";),方向::右=>;println!(";右移";),}}fn main(){Move_Done(Direction::Forward);}。

我们使用::表示法访问Enum内的变量。尝试通过调用“Move_Droone(Direction::Up)”或将“Down”作为新项添加到Direction枚举中来编辑此代码。在第一种情况下,编译器会抛出一个错误,指出在“Direction”中找不到“Up”,在第二种情况下,编译器会抱怨我们没有在Match块中覆盖“Down”。

Rust Enums可以做的不仅仅是充当一组常量-我们还可以将数据与Enum变量相关联。

枚举方向{向前,向后,左,右,}枚举操作{PowerOn,PowerOff,Move(Direction),Rotate,TakePhoto{IS_SCOWARE:BOOL,ZOOM_LEVEL:I32},}FN OPERATE_DRONE(OPERATION:OPERATION){匹配操作{Operation::PowerOn=>;println!(";打开";),操作::PowerOff=>;println!(";Power。),操作::MOVE(方向)=>;MOVE_DRONE(方向),操作::Rotate=>;println!(";Rotate";),操作::TakePhoto{IS_LANCES,ZOOM_LEVEL,}=&gT;println!(";TakePhoto{},{}";,IS_LOCATIONAL,ZOOM_LEVEL),}}FN MOVE_DRONE(方向:方向){匹配方向{方向::向前=>;println!(";向前移动&34;),方向::向后=>;println!(";向后移动&34;),方向::Left=>;println!(";向左移动";),方向::右=>;println!(";Move Right";),}}fn main(){Operate_Droone(Operation::Move(Direction::Forward));Operate_Droone(Operation::TakePhoto{IS_LAWAGE:TRUE,ZOOM_LEVEL:10,})}

这里,我们又添加了一个名为Operation的Enum,它包含“unit like”变量(PowerOn、PowerOff、Rotate)和“struct like”变量(Move、TakePhoto)。请注意我们如何将模式匹配与析构和变量绑定一起使用。

如果您使用过TypeScript或Flow,这类似于区分的联合或SUM类型:

接口{Kind:";PowerOff";;}接口{Kind:";PowerOff";;}type Direction=";Forward";|";|";Left";|";Right";;interface{Kind:";Move";;Direction:Direction;}接口{Kind:";Rotate";;}interface{Kind:";TakePhoto";;IS_LOCATIONAL:Boolean;ZOOM_LEVEL:NUMBER;}type Operation=PowerOn|PowerOff|Move|Rotate|TakePhoto;Function Operate_Done(operation:Operation){Switch(operation.Kind){case";PowerOn";:Console。日志(";开机";);中断;案例";电源关闭";:控制台。LOG(";Power Off";);Break;Case";Move";:MOVE_DRONE(操作.Direction);Break;Case";Rotate";:Console。Log(";Rotate";);Break;Case";TakePhoto";:控制台。log(`TakePhoto${operation.is_scape},${operation.zoom_level}`);Break;}}函数MOVE_DRONE(方向:方向){Switch(方向){case";Forward";:控制台。日志(";向前";);中断;案例";向后";:控制台。Log(";向后移动";);Break;Case";Left";:控制台。LOG(";Move Left";);Break;Case";Right";:控制台。log(";Move Right";);Break;}}OPERATE_DRONE({KIND:";MOVE";,方向:";FORWARD";,});OPERATE_DRONE({KIND:";TakePhoto";,IS_LANCEL:TRUE,ZOOM_LEVEL:10,});

我们在第2章中了解了选项类型。选项实际上是一个具有两个变体的枚举-一些和没有:

fn read_file(path:&;str)->;option<;&;str>;{let content=";hello";if path!=";";{Return Some(Contents);}return None;}fn main(){let file=read_file(";path/to/file";);if file。is_ome(){让Contents=file.。展开();println!(";{}&34;,Contents);}否则{println!(";Empty!";);}}。

fn main(){let file=read_file(";path/to/file";);匹配文件{Some(Contents)=>;println!(";{}";,Contents),None=>;println!(";Empty!";),}}。

在Github、Twitter或LinkedIn上找到我。请随时通过sheshbabu[at]gmail.com与我联系