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

商城火车站权威的赣州网站建设

商城火车站,权威的赣州网站建设,wordpress 图床加速,怎样建立一个免费的网站目录 枚举和模式匹配枚举的定义Option 枚举控制流运算符 match简洁控制流 if let 枚举和模式匹配 枚举的定义 结构体给予你将字段和数据聚合在一起的方法#xff0c;像 Rectangle 结构体有 width 和 height 两个字段。而枚举给予你一个途径去声明某个值是一个集合中的一员。… 目录 枚举和模式匹配枚举的定义Option 枚举控制流运算符 match简洁控制流 if let 枚举和模式匹配 枚举的定义 结构体给予你将字段和数据聚合在一起的方法像 Rectangle 结构体有 width 和 height 两个字段。而枚举给予你一个途径去声明某个值是一个集合中的一员。 假设我们要处理 IP 地址。目前被广泛使用的两个主要 IP 标准IPv4 和 IPv6。这是程序可能会遇到的所有可能的 IP 地址类型所以可以枚举出所有可能的值这也正是此枚举名字的由来。 任何一个 IP 地址要么是 IPv4 的要么是 IPv6 的而且不能两者都是。IP 地址的这个特性使得枚举数据结构非常适合这个场景因为枚举值只可能是其中一个成员。IPv4 和 IPv6 从根本上讲仍是 IP 地址所以当代码在处理适用于任何类型的 IP 地址的场景时应该把它们当作相同的类型。 可以通过在代码中定义一个 IpAddrKind 枚举来表现这个概念并列出可能的 IP 地址类型V4 和 V6。这被称为枚举的 成员 enum IpAddrKind {V4,V6, }现在 IpAddrKind 就是一个可以在代码中使用的自定义数据类型了。这样就可以创建 IpAddrKind 两个不同成员的实例 let four IpAddeKind::V4; let six IpAddeKind::V6;注意枚举的成员位于其标识符的命名空间中并使用两个冒号分开。这么设计的益处是现在 IpAddrKind::V4 和 IpAddrKind::V6 都是 IpAddrKind 类型的。例如接着可以定义一个函数来接收任何 IpAddrKind类型的参数使用任一成员来调用这个函数 enum IpAddrKind {V4,V6, }fn main() {let four IpAddrKind::V4;let six IpAddrKind::V6;route(IpAddrKind::V4);route(IpAddrKind::V6); }fn route(ip_kind: IpAddrKind) {}使用枚举甚至还有更多优势。进一步考虑一下我们的 IP 地址类型目前没有一个存储实际 IP 地址数据的方法只知道它是什么类型的。可以使用结构体来解决这个问题 #[derive(Debug)] enum IpAddrKind {V4,V6, }#[derive(Debug)] struct IpAddr {kind: IpAddrKind,address: String, }fn main() {let home IpAddr {kind: IpAddrKind::V4,address: String::from(127.0.0.1),};let loopback IpAddr {kind: IpAddrKind::V6,address: String::from(::1),};println!({:?}, home);println!({:?}, loopback); }这里定义了一个有两个字段的结构体 IpAddrIpAddrKind之前定义的枚举类型的 kind字段和 String 类型 address 字段。有这个结构体的两个实例。第一个home它的 kind的值是 IpAddrKind::V4 与之相关联的地址数据是 127.0.0.1。第二个实例loopbackkind的值是 IpAddrKind 的另一个成员V6关联的地址是 ::1。使用了一个结构体来将 kind和 address 打包在一起现在枚举成员就与值相关联了。 还可以使用一种更简洁的方式来表达相同的概念仅仅使用枚举并将数据直接放进每一个枚举成员而不是将枚举作为结构体的一部分。IpAddr 枚举的新定义表明了 V4 和 V6 成员都关联了 String值 #[derive(Debug)] enum IpAddrKind {V4(String),V6(String), }fn main() {let home IpAddrKind::V4(String::from(127.0.0.1));let loopback IpAddrKind::V6(String::from(::1));println!({:?}, home);println!({:?}, loopback); }直接将数据附加到枚举的每个成员上这样就不需要一个额外的结构体了。这里也很容易看出枚举工作的另一个细节每一个定义的枚举成员的名字也变成了一个构建枚举的实例的函数。也就是说IpAddr::V4() 是一个获取 String 参数并返回 IpAddr 类型实例的函数调用。作为定义枚举的结果这些构造函数会自动被定义。 用枚举替代结构体还有另一个优势每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果想要将 V4 地址存储为四个 u8 值而 V6 地址仍然表现为一个 String这就不能使用结构体了。枚举则可以轻易的处理这个情况 #[derive(Debug)] enum IpAddrKind {V4(u8, u8, u8, u8),V6(String), }fn main() {let home IpAddrKind::V4(127,0,0,1);let loopback IpAddrKind::V6(String::from(::1));println!({:?}, home);println!({:?}, loopback); }这些代码展示了如何用枚举来表示两种类型的 IP 地址。虽然这种做法是有效的但由于存储和处理 IP 地址在实际开发中非常常见Rust 的标准库早已为我们提供了一个现成的解决方案。标准库中的 IpAddr 枚举与我们自定义的非常相似但它更进一步将每种 IP 类型分别封装在专门的结构体中从而更清晰地区分不同格式的 IP 地址 #![allow(unused)] fn main() { struct Ipv4Addr {// --snip-- }struct Ipv6Addr {// --snip-- }enum IpAddr {V4(Ipv4Addr),V6(Ipv6Addr), } }这些代码展示了可以将任意类型的数据放入枚举成员中例如字符串、数字类型或者结构体。甚至可以包含另一个枚举另外标准库中的类型通常并不比你设想出来的要复杂多少。 注意虽然标准库中包含一个 IpAddr 的定义仍然可以创建和使用我们自己的定义而不会有冲突因为我们并没有将标准库中的定义引入作用域。 枚举的成员中可以内嵌多种多样的类型 enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32), }这个枚举有四个含有不同类型的成员 Quit 没有关联任何数据。Move 类似结构体包含命名字段。Write 包含单独一个 String。ChangeColor 包含三个 i32。 定义一个这样的有关联值的枚举的方式和定义多个不同类型的结构体的方式很相像除了枚举不使用 struct 关键字以及其所有成员都被组合在一起位于 Message 类型下。如下这些结构体可以包含与之前枚举成员中相同的数据 struct QuitMessage; // 类单元结构体 struct MoveMessage {x: i32,y: i32, } struct WriteMessage(String); // 元组结构体 struct ChangeColorMessage(i32, i32, i32); // 元组结构体不过如果使用不同的结构体由于它们都有不同的类型将不能像使用定义的 Message 枚举那样轻易的定义一个能够处理这些不同类型的结构体的函数因为枚举是单独一个类型。 结构体和枚举还有另一个相似点就像可以使用 impl 来为结构体定义方法那样也可以在枚举上定义方法。这是一个定义于 Message 枚举上的叫做 call 的方法 #[derive(Debug)] enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32), }impl Message {fn call(self) {println!({:?}, self)} }fn main() {let m Message::Write(String::from(hello));m.call(); }方法体使用了 self 来获取调用方法的值。这个例子中创建了一个值为 Message::Write(String::from(hello)) 的变量 m而且这就是当 m.call() 运行时 call 方法中的 self 的值。 Option 枚举 Option 是标准库定义的另一个枚举。Option 类型应用广泛因为它编码了一个非常普遍的场景即一个值要么有值要么没值。 例如如果请求一个非空列表的第一项会得到一个值如果请求一个空的列表就什么也不会得到。从类型系统的角度来表达这个概念就意味着编译器需要检查是否处理了所有应该处理的情况这样就可以避免在其他编程语言中非常常见的 bug。 编程语言的设计经常要考虑包含哪些功能但考虑排除哪些功能也很重要。Rust 并没有很多其他语言中有的空值功能。空值是一个值它代表没有值。在有空值的语言中变量总是这两种状态之一空值和非空值。 空值的问题在于当你尝试像一个非空值那样使用一个空值会出现某种形式的错误。因为空和非空的属性无处不在非常容易出现这类错误。 然而空值尝试表达的概念仍然是有意义的空值是一个因为某种原因目前无效或缺失的值。 问题不在于概念而在于具体的实现。为此Rust 并没有空值不过它确实拥有一个可以编码存在或不存在概念的枚举。这个枚举是 OptionT而且它定义于标准库中如下 enum OptionT {None,Some(T), }OptionT 枚举是如此有用以至于它甚至被包含在了 prelude 之中你不需要将其显式引入作用域。另外它的成员也是如此可以不需要 Option:: 前缀来直接使用 Some 和 None。即便如此 OptionT 也仍是常规的枚举Some(T) 和 None 仍是 OptionT 的成员。 T 是一个泛型类型参数目前只需要知道的就是 T 意味着 Option 枚举的 Some 成员可以包含任意类型的数据同时每一个用于 T 位置的具体类型使得 OptionT 整体作为不同的类型。这里是一些包含数字类型和字符串类型 Option 值的例子 fn main() {let some_number Some(5);let some_string Some(a string);let absent_number: Optioni32 None;println!({:?}, some_number);println!({:?}, some_string);println!({:?}, absent_number); }some_number 的类型是 Optioni32。some_char 的类型是 Optionchar是不同于some_number的类型。因为在 Some 成员中指定了值Rust 可以推断其类型。对于 absent_numberRust 需要指定 Option 整体的类型因为编译器只通过 None 值无法推断出 Some 成员保存的值的类型。这里我们告诉 Rust 希望 absent_number 是 Optioni32 类型的。 当有一个 Some 值时就知道存在一个值而这个值保存在 Some 中。当有个 None 值时在某种意义上它跟空值具有相同的意义并没有一个有效的值。那么OptionT 为什么就比空值要好呢 简而言之因为 OptionT 和 T这里 T 可以是任何类型是不同的类型编译器不允许像一个肯定有效的值那样使用 OptionT。例如这段代码不能编译因为它尝试将 Optioni8 与 i8相加 fn main() {let x: i8 5;let y: Optioni8 Some(5);let sum x y; }运行这段代码将会产生错误信息 error[E0277]: cannot add Optioni8 to i8-- src/main.rs:46:17| 46 | let sum x y;| ^ no implementation for i8 Optioni8| help: the trait AddOptioni8 is not implemented for i8 help: the following other types implement trait AddRhs:i8 implements Addi8i8 implements Addi8 implements Addi8i8 implements Add这意味着 Rust 不知道该如何将 Optioni8 与 i8 相加因为它们的类型不同。当在 Rust 中拥有一个像 i8 这样类型的值时编译器确保它总是有一个有效的值。这样可以自信使用而无需做空值检查。只有当使用 Optioni8或者任何用到的类型的时候需要担心可能没有值而编译器会确保在使用值之前处理了为空的情况。 换句话说在对 OptionT 进行运算之前必须将其转换为 T。通常这能帮助开发者捕获到空值最常见的问题之一假设某值不为空但实际上为空的情况。 Option 枚举最常见的应用场景就是函数可能返回空值如下面代码所示 fn find_user(id: u32) - OptionString {if id 1 {Some(Alice.to_string())} else {None} }fn main() {if let Some(name) find_user(1) {println!(User: {}, name);} else {println!(User not found.);} }用途 比如查数据库、查列表、查配置时找不到返回 None找到了返回 Some(value)。 控制流运算符 match Rust 有一个叫做 match 的极为强大的控制流运算符它允许我们将一个值与一系列的模式相比较并根据相匹配的模式执行相应代码。模式可由字面值、变量、通配符和许多其他内容构成。match 的力量来源于模式的表现力以及编译器检查它确保了所有可能的情况都得到处理。 可以把 match 表达式想象成某种硬币分类器硬币滑入有着不同大小孔洞的轨道每一个硬币都会掉入符合它大小的孔洞。同样地值也会通过 match 的每一个模式并且在遇到第一个 “符合” 的模式时值会进入相关联的代码块并在执行中被使用 enum Coin {Penny,Nickel,Dime,Quarter, }fn value_in_cents(coin: Coin) - u8 {match coin {Coin::Penny 1,Coin::Nickel 5,Coin::Dime 10,Coin::Quarter 25,} }fn main() {println!({}, value_in_cents(Coin::Penny));println!({}, value_in_cents(Coin::Nickel));println!({}, value_in_cents(Coin::Dime));println!({}, value_in_cents(Coin::Quarter)); }拆开 value_in_cents 函数中的 match 来看。首先列出 match 关键字后跟一个表达式在这个例子中是 coin 的值。这看起来非常像 if 所使用的条件表达式不过这里有一个非常大的区别对于 if表达式必须返回一个布尔值而这里它可以是任何类型的。 接下来是 match 的分支。一个分支有两个部分一个模式和一些代码。第一个分支的模式是值 Coin::Penny 而之后的 运算符将模式和将要运行的代码分开。每一个分支之间使用逗号分隔。 当 match 表达式执行时它将结果值按顺序与每一个分支的模式相比较。如果模式匹配了这个值这个模式相关联的代码将被执行。如果模式并不匹配这个值将继续执行下一个分支非常类似一个硬币分类器。可以拥有任意多的分支。 每个分支相关联的代码是一个表达式而表达式的结果值将作为整个 match 表达式的返回值。 如果分支代码较短的话通常不使用大括号正如每个分支都只是返回一个值。如果想要在分支中运行多行代码可以使用大括号而分支后的逗号是可选的。例如如下代码在每次使用Coin::Penny 调用时都会打印出 “Lucky penny!”同时仍然返回代码块最后的值1 fn value_in_cents(coin: Coin) - u8 {match coin {Coin::Penny {println!(Lucky penny!);1}Coin::Nickel 5,Coin::Dime 10,Coin::Quarter 25,} }匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值。 1999 年到 2008 年间美国在 25 美分的硬币的一侧为 50 个州的每一个都印刷了不同的设计。其他的硬币都没有这种区分州的设计所以只有这些 25 美分硬币有特殊的价值。可以将这些信息加入一个 enum通过改变 Quarter 成员来包含一个 State 值 #[derive(Debug)] enum UsState {Alabama,Alaska, }enum Coin {Penny,Nickel,Dime,Quarter(UsState), }fn value_in_cents(coin: Coin) - u8 {match coin {Coin::Penny 1,Coin::Nickel 5,Coin::Dime 10,Coin::Quarter(state) {println!(State quarter from {:?}!, state);25}} }fn main() {let value value_in_cents(Coin::Quarter(UsState::Alaska));println!({:?}, value); }如果调用 value_in_cents(Coin::Quarter(UsState::Alaska))coin 将是 Coin::Quarter(UsState::Alaska)。当将值与每个分支相比较时没有分支会匹配直到遇到 Coin::Quarter(state)。这时state 绑定的将会是值 UsState::Alaska。接着就可以在 println! 表达式中使用这个绑定了像这样就可以获取 Coin 枚举的 Quarter 成员中内部的州的值。 在之前的部分中使用 OptionT 时是为了从 Some 中取出其内部的 T 值还可以像处理 Coin 枚举那样使用 match 处理 OptionT只不过这回比较的不再是硬币而是 OptionT的成员但 match 表达式的工作方式保持不变。 比如想要编写一个函数它获取一个 Optioni32 如果其中含有一个值将其加一。如果其中没有值函数应该返回 None 值而不尝试执行任何操作 fn main() {let five Some(5);let six plus_one(five);let none plus_one(None);println!({:?}, {:?}, {:?}, five, six, none); } fn plus_one(x: Optioni32) - Optioni32 {match x {None None,Some(i) Some(i 1),} }match 还有另一方面需要讨论这些分支必须覆盖了所有的可能性。考虑一下 plus_one 函数的这个版本它有一个 bug 并不能编译 fn main() {let five Some(5);let six plus_one(five);let none plus_one(None);println!({:?}, {:?}, {:?}, five, six, none); } fn plus_one(x: Optioni32) - Optioni32 {match x {Some(i) Some(i 1),} }没有处理 None 的情况所以这些代码会造成一个 bug。幸运的是这是一个 Rust 知道如何处理的 bug。如果尝试编译这段代码会得到这个错误 error[E0004]: non-exhaustive patterns: None not covered-- src/main.rs:8:11| 8 | match x {| ^ pattern None not covered| note: Optioni32 defined here-- /Users/huangruibang/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:572:1| 572 | pub enum OptionT {| ^^^^^^^^^^^^^^^^^^ ... 576 | None,| ---- not covered note: the matched value is of type Optioni32 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown| 9 ~ Some(i) Some(i 1), 10 ~ None todo!(),|Rust 知道没有覆盖所有可能的情况甚至知道哪些模式被忘记了。Rust 中的匹配是穷尽的必须穷举到最后的可能性来使代码有效。特别的在这个 OptionT 的例子中Rust 防止开发者忘记明确的处理 None 的情况这让开发者免于假设拥有一个实际上为空的值从而使错误不可能发生。 有时候只需要对特定的值采取特殊操作其他的值采取默认操作就可以通过通配模式——将匹配到的默认值绑定为 other 来实现。例如只在 1、3、5、7 的时候有输出其它数字都不进行操作 fn main() {let arr [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];for element in arr {match element {1 println!(One),3 println!(Three),5 println!(Five),7 println!(Seven),other println!(Other),}} }除了用通配模式还可以用占位符 _ 来实现 fn main() {let arr [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];for element in arr {match element {1 println!(One),3 println!(Three),5 println!(Five),7 println!(Seven),_ println!(Other),}} }简洁控制流 if let 在数字 1-10 中随机生成一个数只有生成 6 才会显示 “You win!”用 match 的代码如下 use rand::Rng; fn main() {let number rand::thread_rng().gen_range(1..10);println!({}, number);match number {6 println!(You win!),_ (),} }if let 语法以一种不那么冗长的方式结合 if 和 let来处理只匹配一个模式的值而忽略其他模式的情况 use rand::Rng; fn main() {let number rand::thread_rng().gen_range(1..10);println!({}, number);if let 6 number {println!(You win!);}; }if let 语法获取通过等号分隔的一个模式和一个表达式。它的工作方式与 match 相同这里的表达式对应 match 而模式则对应第一个分支。模式不匹配时 if let 块中的代码不会执行。 使用 if let 意味着编写更少代码更少的缩进和更少的样板代码。然而这样会失去 match 强制要求的穷尽性检查。match 和 if let 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。 换句话说可以认为 if let 是 match 的一个语法糖它当值匹配某一模式时执行代码而忽略所有其他值。 可以在 if let 中包含一个 else。else 块中的代码与 match 表达式中的 _ 分支块中的代码相同这样的 match 表达式就等同于 if let 和 else。 生成非 6 的数字显示 “You lose!” use rand::Rng; fn main() {let number rand::thread_rng().gen_range(1..10);println!({}, number);if let 6 number {println!(You win!);}else { println!(You lose!);} }if let–else 是 match 的简化版类似 if–else但专门匹配特定模式更适合只关心一种匹配的情况。
http://www.zqtcl.cn/news/185798/

相关文章:

  • 怎做连接网站wordpress iis设置方法
  • ugc网站开发网站设计常见流程
  • dz论坛可以做招聘网站国内空间没备案可以打开网站吗
  • 建设用地规划证查询网站公司起名字大全免费好听
  • 杭州网站建设公司有哪些瑞诺国际的数字营销模式
  • 宣城网站建设 有限公司高州做网站
  • 做外贸最适合的网站系统有可以做国外支付系统的网站吗
  • 建设执业资格注册中心网站办事大厅ui设计素材库
  • 个人网站免费建站4399电脑版网页链接
  • 重庆开县网站建设公司推荐网站建设与维护高职
  • 关于网站开发的技术博客海口网站设计建设
  • xx市院门户网站建设方案做视频特技的网站
  • 肇庆seo公司咨询23火星seo 网站
  • 天元建设集团有限公司破产新手seo网站做什么类型好
  • spa.net网站开发二次开发需要什么
  • 如何做网站静态页面商丘网签查询
  • 网站建设好学么模版型网站是怎样的
  • 网站维护建设费应计入科目高端营销型网站制作
  • 推荐几个好的网站wordpress 加载数据库表格也卖弄
  • 承德网站开发找人做网站安全吗
  • 百度网站推广电话眼镜网站怎么做竞价
  • 邢台建设银行官方网站为什么建设网站很多公司没有
  • 闵行做网站费用湖南正规网络营销哪家便宜
  • 找个公司做网站需要注意什么wordpress用户名长度
  • 推荐几个没封的正能量网站营销技巧和营销方法视频
  • html mip 网站桂林市临桂区
  • 做网站如何月入10万建行app怎么注册登录
  • 建设一个旅游网站毕业设计建设网站的功能定位是什么原因
  • wordpress网站导航模板杭州建设网站的公司
  • 如何做视频解析网站wordpress 关闭评论