北京网站建设的关键词重庆建设企业网站

当前位置: 首页 > news >正文

北京网站建设的关键词,重庆建设企业网站,国外大气网站欣赏,wordpress调查问卷插件模式匹配 模式匹配是从函数式编程语言#xff08;例如#xff1a;Haskell#xff0c;Lisp#xff09;吸收而来的#xff0c;用于为复杂的类型系统提供一个轻松的解构能力。rust使用match来提供模式匹配的功能。mathc类似于其它编程语言中的switch-case#xff0c;但是远…模式匹配 模式匹配是从函数式编程语言例如HaskellLisp吸收而来的用于为复杂的类型系统提供一个轻松的解构能力。rust使用match来提供模式匹配的功能。mathc类似于其它编程语言中的switch-case但是远比switch-case强大。match的通用模式如下所示。 match target {模式1 表达式1,模式2 {语句1;语句2;表达式2},_ 表达式3 }该形式清晰的说明了何为模式何为模式匹配将模式与 target 进行匹配即为模式匹配而模式匹配不仅仅局限于 match还有if let。一个实际的例子如下所示 fn main() {enum Coin {Penny,Nickel,Dime,Quarter,}fn value_in_cents(coin: Coin) - u8 {match coin {Coin::Penny {println!(Lucky penny!);1},Coin::Nickel 5,Coin::Dime 10,Coin::Quarter 25,}}let coin Coin::Dime;let x value_in_cents(coin);println!({}, x); }match 的匹配必须要穷举出所有可能因此这里用 _ 来代表未列出的所有可能性。当我们不想使用通配模式获取的值时请使用 _ 这是一个特殊的模式可以匹配任意值而不绑定到该值。这告诉 Rust 我们不会使用这个值所以 Rust 也不会警告我们存在未使用的变量。match 的每一个分支都必须是一个表达式且所有分支的表达式最终返回值的类型必须相同match 的模式之间可以使用X | Y类似逻辑或代表该分支可以匹配 X 也可以匹配 Y只要满足一个即可 match表达式在执行时将目标值coin按照顺序依次与每一个分支的模式相比较如果模式匹配了这个值那么模式之后的代码将被执行。如果模式并不匹配这个值将继续执行下一个分支。每个分支相关联的代码是一个表达式而表达式的结果值将作为整个 match 表达式的返回值。如果分支有多行代码那么需要用 {} 包裹同时最后一行代码需要是一个表达式。 使用match表达式赋值 #![allow(unused)] enum IpAddr {Ipv4,Ipv6 }fn main() {let ip1 IpAddr::Ipv6;let ipstr match ip1 {IpAddr::Ipv4 127.0.0.1, ::1,};println!({}, ip_str); }通过match表达式给ip_str赋值绑定了一个Ipv6的地址::1环回地址。 模式匹配取出值 模式匹配的另外一个重要功能是从模式中取出绑定的值。例如 enum Action {Say(String),MoveTo(i32, i32),ChangeColorRGB(u16, u16, u16), }fn main() {let actions [Action::Say(Hello Rust.to_string()),Action::MoveTo(1,2),Action::ChangeColorRGB(255,255,0),];for action in actions {match action {Action::Say(s) { //当action匹配Action::Say的时候s将会取出取出其中的值println!({}, s);},Action::MoveTo(x, y) { // 也可以取出多个值println!(point from (0, 0) move to ({}, {}), x, y);},Action::ChangeColorRGB(r, g, ) {println!(change color into (r:{}, g:{}, b:0), b has been ignored,r, g,);}}} }运行后输出如下所示 Hello Rust point from (0, 0) move to (1, 2)
change color into (r:255, g:255, b:0), b has been ignored通配符(
) 当我们不想在匹配时列出所有值的时候可以使用 Rust 提供的一个特殊模式例如u8 可以拥有 0 到 255 的有效的值但是我们只关心 1、3、5 和 7 这几个值不想列出其它的 0、2、4、6、8、9 一直到 255 的值。例如 let some_u8_value 0u8; match some_u8value {1 println!(one),3 println!(three),5 println!(five),7 println!(seven), (), }通过将 _ 其放置于其他分支后_ 将会匹配所有遗漏的值。() 表示返回单元类型与所有分支返回值的类型相同因为println!宏返回()所以当匹配到 _ 后什么也不会发生。 if let匹配 在某些场景下我们其实只关心某一个值是否存在此时 match 就显得过于啰嗦。例如 let v Some(3u8);match v {Some(3) println!(three),_ (),}我们只想要对 Some(3) 模式进行匹配, 不想处理任何其他 Some 值或 None 值。但是为了满足 match 表达式穷尽性的要求写代码时必须在处理完这唯一的成员后加上 _ ()这样会增加不少无用的代码。为此rust提供了更加简洁的if-let匹配上面的例子可以改写为如下所示 if let Some(3) v {println!(three); }matches!宏 Rust 标准库中提供了一个非常实用的宏matches!它可以将一个表达式跟模式进行匹配然后返回匹配的结果 true or false。例如 let foo f; assert!(matches!(foo, A..Z | a..z));let bar Some(4); assert!(matches!(bar, Some(x) if x 2));解构Option 之前在枚举类型中遗留的一个问题是“一个变量要么有值Some(T), 要么为空None”。当时没有取出Some中的值现在有了模式匹配我们来实现取出值。例如: let num Some(123i32); let num1: i32; num1 match num {Some(x) x,None 0, };println!({}, num1);这个例子中我们只是对数值类型做了取出操作对其它类型也是类似的。例如 let s Some(123.to_string()); let s1:String; s1 match s {Some(x) x,None .to_string(), }; println!({}, s1);只不过对于String这种存储在堆内存上的数据类型而言这会导致所有权的转移从而导致s在模式匹配之后无法使用。 模式匹配无处不在 在rust中模式匹配无处不在。除了matchif-let之外。还有while let, for循环let语句函数参数等都是模式匹配。 while let while let条件循环它的作用是只要模式匹配循环就能一直进行。下面是一个例子。 // Vec是动态数组 let mut stack Vec::new();// 向数组尾部插入元素 stack.push(1); stack.push(2); stack.push(3);// stack.pop从数组尾部弹出元素 while let Some(top) stack.pop() {println!({}, top); }pop 方法取出动态数组的最后一个元素并返回 Some(value)如果动态数组是空的将返回 None。如果返回了None那么while循环将会结束。 for循环 let v vec![a, b, c];for (index, value) in v.iter().enumerate() {println!({} is at index {}, value, index); }像这样的for循环本质上也是模式匹配。迭代器每次迭代会返回一个 (索引值) 形式的元组然后用 (index,value) 来匹配。 let语句 实际上let x 3这也是一种模式绑定代表将匹配的值绑定到变量 x 上。更复杂一些的比如元组。let (x, y, z) (1, 2, 3)。因此在 Rust 中变量名也是一种模式。 函数参数 fn print_coordinates((x, y): (i32, i32)) {println!(Current location: ({}, {}), x, y); }fn main() {let point (3, 5);printcoordinates(point); }在函数中参数也是一种模式匹配。(3, 5) 会匹配模式 (x, y)因此 x 得到了 3y 得到了 5。 可驳模式和不可驳模式 在rust中模式匹配可以分为两类一类是可驳模式另一类是不可驳模式。像let, for, match这类属于不可驳模式匹配它们要求必须完全覆盖匹配而if-let, while-let这种属于可驳模式它们允许忽略其余的模式。 通过…匹配值的范围 let x 3;match x {1..3 println!(one through three),4..8 println!(four through seven) println!(something else), }.. 语法允许你匹配一个闭区间序列内的值。序列只允许用于数字或字符类型原因是它们可以连续同时编译器在编译期可以检查该序列是否为空字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。 let x c;match x {a..j println!(early ASCII letter),k..z println!(late ASCII letter),_ println!(something else), }Rust 知道 ‘c’ 位于第一个模式的序列内所以会打印出 early ASCII letter。 解构结构体 struct Point {x: i32,y: i32, }fn main() {let p Point { x: 0, y: 7 };let Point { x: a, y: b } p;assert_eq!(0, a);asserteq!(7, b); }这段代码创建了变量 a 和 b 来匹配结构体 p 中的 x 和 y 字段这个例子展示了模式中的变量名不必与结构体中的字段名一致。不过通常希望变量名与字段名一致以便于理解变量来自于哪些字段。这时候只需要将let Point { x: x, y: y } p;简写为let Point { x, y } p;即可。 也可以使用字面值作为结构体模式的一部分进行解构而不是为所有的字段创建变量。这允许我们测试一些字段为特定值的同时创建其他字段的变量。 fn main() {let p Point { x: 0, y: 7 };match p {Point { x, y: 0 } println!(On the x axis at {}, x),Point { x: 0, y } println!(On the y axis at {}, y),Point { x, y } println!(On neither axis: ({}, {}), x, y),} }在这个例子中首先是 match 第一个分支指定匹配 y 为 0 的 Point 然后第二个分支在第一个分支之后匹配 y 不为 0x 为 0 的 Point; 最后一个分支匹配 x 不为 0y 也不为 0 的 Point。 解构枚举 enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32), }fn main() {let msg Message::ChangeColor(0, 160, 255);match msg {Message::Quit {println!(The Quit variant has no data to destructure.)}Message::Move { x, y } {println!(Move in the x direction {} and in the y direction {},x,y);}Message::Write(text) println!(Text message: {}, text),Message::ChangeColor(r, g, b) {println!(Change the color to red {}, green {}, and blue {},r,g,b)}} }模式匹配一样要类型相同因此匹配 Message::Move{1,2} 这样的枚举值就必须要用 Message::Move{x,y} 这样的同类型模式才行。 这段代码会打印出 Change the color to red 0, green 160, and blue 255。尝试改变 msg 的值来观察其他分支代码的运行。 对于像 Message::Quit 这样没有任何数据的枚举成员不能进一步解构其值。只能匹配其字面值 Message::Quit因此模式中没有任何变量。 对于另外两个枚举成员就用相同类型的模式去匹配出对应的值即可。 解构嵌套的结构体和枚举 #![allow(unused)] enum Color {Rgb(i32, i32, i32),Hsv(i32, i32, i32), }enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(Color), }fn main() {let msg Message::ChangeColor(Color::Hsv(0, 160, 255));match msg {Message::ChangeColor(Color::Rgb(r, g, b)) {println!(Change the color to red {}, green {}, and blue {},r,g,b)}Message::ChangeColor(Color::Hsv(h, s, v)) {println!(Change the color to hue {}, saturation {}, and value {},h,s,v)} ()} }模式匹配非常强大像这样嵌套的结构体和枚举它也能进行解构取出嵌套在其中的值。下面是一个更复杂的例子 #![allow(unused)] fn main() { struct Point {x: i32,y: i32,}let ((feet, inches), Point {x, y}) ((3, 10), Point { x: 3, y: -10 }); }结构体和元组嵌套在元组中rust依旧可以将这种复杂类型分解匹配从而让我们取出感兴趣的值。 解构数组 对于数组我们可以用类似元组的方式解构分为两种情况 定长数组 let arr: [u16; 2] [114, 514]; let [x, y] arr;assert_eq!(x, 114); assert_eq!(y, 514);不定长数组 let arr: [u16] [114, 514];if let [x, ..] arr {assert_eq!(x, 114); }if let [.., y] arr {assert_eq!(y, 514); }let arr: [u16] [];assert!(matches!(arr, [..])); assert!(!matches!(arr, [x, ..]));..是用来忽略剩余值的后续会介绍。 忽略模式中的值 有时忽略模式中的一些值是很有用的比如在 match 中的最后一个分支使用 _ 模式匹配所有剩余的值。 你也可以在另一个模式中使用 _ 模式使用一个以下划线开始的名称或者使用 … 忽略所剩部分的值。 使用 _ 忽略整个值 虽然 _ 模式作为 match 表达式最后的分支特别有用但是它的作用还不限于此。例如可以将其用于函数参数中 fn foo(_: i32, y: i32) {println!(This code only uses the y parameter: {}, y); }fn main() {foo(3, 4); }此时编译器就不会警告说存在未使用的函数参数就跟使用命名参数一样。 使用嵌套的 _ 忽略部分值 let mut setting_value Some(5); let new_setting_value Some(10);match (setting_value, new_settingvalue) {(Some(), Some()) {println!(Cant overwrite an existing customized value);} {setting_value new_setting_value;} }println!(setting is {:?}, setting_value);第一个匹配分支我们不关心里面的值只关心元组中两个元素的类型因此对于 Some 中的值直接进行忽略。 剩下的形如 (Some(),None)(None, Some()), (None,None) 形式都由第二个分支 _ 进行分配。还可以在一个模式中的多处使用下划线来忽略特定值如下所示这里忽略了一个五元元组中的第二和第四个值 let numbers (2, 4, 8, 16, 32);match numbers {(first, _, third, , fifth) {println!(Some numbers: {}, {}, {}, first, third, fifth)}, }用 … 忽略剩余值 前文的不定长数组的模式中出现了..用来忽略除开头以外的值或者是除结尾以外的值。当然了..也可以忽略中间的某些值。例如 fn main() {let numbers (2, 4, 8, 16, 32);match numbers {(first, .., last) {println!(Some numbers: {}, {}, first, last);},} }这里用 first 和 last 来匹配第一个和最后一个值。… 将匹配并忽略中间的所有值。然而使用 … 必须是无歧义的。如果期望匹配和忽略的值是不明确的Rust 会报错。下面代码展示了一个带有歧义的 … 例子因此不能编译 fn main() {let numbers (2, 4, 8, 16, 32);match numbers {(.., second, ..) {println!(Some numbers: {}, second)},} }Rust 无法判断second 应该匹配 numbers 中的第几个元素因此无法通过编译。 匹配守卫 匹配守卫match guard是一个位于 match 分支模式之后的额外 if 条件它能为分支模式提供更进一步的匹配条件。 这个条件可以使用模式中创建的变量 let num Some(4);match num {Some(x) if x 5 println!(less than five: {}, x),Some(x) println!({}, x),None (), }这个例子会打印出 less than five: 4。当 num 与模式中第一个分支匹配时Some(4) 可以与 Some(x) 匹配接着匹配守卫检查 x 值是否小于 5因为 4 小于 5所以第一个分支被选择。模式中无法提供类如 if x 5 的表达能力我们可以通过匹配守卫的方式来实现。 match 表达式的模式中新建了一个变量而不是使用 match 之外的同名变量。内部变量覆盖了外部变量意味着此时不能够使用外部变量的值下面代码展示了如何使用匹配守卫修复这个问题。 fn main() {let x Some(5);let y 10;match x {Some(50) println!(Got 50),Some(n) if n y println!(Matched, n {}, n), println!(Default case, x {:?}, x),}println!(at the end: x {:?}, y {}, x, y); }匹配守卫 if n y 并不是一个模式所以没有引入新变量。这个 y 正是 外部的 y 而不是新的覆盖变量 y这样就可以通过比较 n 和 y 来表达寻找一个与外部 y 相同的值的概念了。 也可以在匹配守卫中使用 或 运算符 | 来指定多个模式同时匹配守卫的条件会作用于所有的模式。下面代码展示了匹配守卫与 | 的优先级。这个例子中看起来好像 if y 只作用于 6但实际上匹配守卫 if y 作用于 4、5 和 6 在满足 x 属于 4 | 5 | 6 后才会判断 y 是否为 true let x 4; let y false;match x {4 | 5 | 6 if y println!(yes),_ println!(no), }这个匹配条件表明此分支只匹配 x 值为 4、5 或 6 同时 y 为 true 的情况。 绑定 允许为一个字段绑定另外一个变量。下面例子中我们希望测试 Message::Hello 的 id 字段是否位于 3…7 范围内同时也希望能将其值绑定到 id_variable 变量中以便此分支中相关的代码可以使用它。 enum Message {Hello { id: i32 }, }let msg Message::Hello { id: 5 };match msg {Message::Hello { id: id_variable 3..7 } {println!(Found an id in range: {}, id_variable)},Message::Hello { id: 10..12 } {println!(Found an id in another range)},Message::Hello { id } {println!(Found some other id: {}, id)}, }上例会打印出 Found an id in range: 5。通过在 3…7 之前指定 id_variable 我们捕获了任何匹配此范围的值并同时将该值绑定到变量 idvariable 上。 使用 还可以在绑定新变量的同时对目标进行解构 #[derive(Debug)] struct Point {x: i32,y: i32, }fn main() {// 绑定新变量 p同时对 Point 进行解构let p Point {x: px, y: py } Point {x: 10, y: 23};println!(x: {}, y: {}, px, py);println!({:?}, p);let point Point {x: 10, y: 5};if let p Point {x: 10, y} point {println!(x is 10 and y is {} in {:?}, y, p);} else {println!(x was not 10 :();} }的新特性rust1.53新增 fn main() {match 1 {num (1 | 2) {println!({}, num);} {}} }参考资料 Rust语言圣经Rust程序设计语言