阳江市做网站开源的网站开发软件

diannao/2025/10/24 13:49:15/文章来源:
阳江市做网站,开源的网站开发软件,十大利润最高的实体店,文字一键生成图片上一篇#xff1a; 04-了解所有权 结构体#xff08;struct#xff09;是一种自定义数据类型#xff0c;可以将多个相关值打包命名#xff0c;组成一个有意义的组。如果你熟悉面向对象的语言#xff0c;那么结构体就像是对象的数据属性。在本章中#xff0c;我们将对元组…上一篇 04-了解所有权 结构体struct是一种自定义数据类型可以将多个相关值打包命名组成一个有意义的组。如果你熟悉面向对象的语言那么结构体就像是对象的数据属性。在本章中我们将对元组和结构体进行对比在已有知识的基础上说明结构体是更好的数据分组方式。 我们将演示如何定义和实例化结构体。我们将讨论如何定义关联函数尤其是称为方法methods的关联函数以指定与结构体类型相关的行为。结构体和枚举是在程序域中创建新类型的构件可以充分利用 Rust 的编译时类型检查功能。 1. 定义和实例化结构体 结构体与 元组类型 一节中讨论的元组类似都可以保存多个相关值。与元组一样结构体中的数据也可以是不同的类型。与元组不同的是在结构体中我们会为每块数据命名这样就能清楚地知道这些值的含义。添加这些名称意味着结构体比元组更灵活您不必依赖数据的顺序来指定或访问实例的值。 要定义结构体我们需要输入关键字 struct 并为整个结构体命名。结构体的名称应描述被组合在一起的数据块的意义。然后在大括号内定义数据的名称和类型我们称之为字段。例如下列显示了一个存储用户账户信息的结构体。 struct User {active: bool,username: String,email: String,sign_in_count: u64, }要在定义结构体后使用该结构体我们需要为每个字段指定具体的值从而创建该结构体的实例。在创建实例时我们先声明结构体的名称然后添加包含Key-Value”的大括号其中Key是字段的名称Value是我们要存储在这些字段中的数据。我们不必按照在结构体中声明字段的顺序指定字段。换句话说结构体的定义就像是该类型的通用模板而实例则在该模板中填入特定数据以创建该类型的值。下面代码所示声明一个特定的用户 fn main() {let user1 User {active: true,username: String::from(someusername123),sign_in_count: 1,email: String::from(someoneexample.com),}; }struct User {active: bool,username: String,email: String,sign_in_count: u64, }; 我们尝试编译它但会出错 cargo.exe buildCompiling structType v0.1.0 (E:\rustProj\structType) error: expected item, found ;-- src\main.rs:15:2| 15 | };| ^ help: remove this semicolon| help: braced struct declarations are not followed by a semicolonerror: could not compile structType (bin structType) due to previous error Rust编译器告诉我们Struct声明类型是结尾是不需要分号; 这点在语法上与C/C不同 要从结构体中获取特定值我们使用点符号。例如要访问该用户的电子邮件地址我们使用 user1.email 。如果实例是可变的我们就可以通过使用点符号并将其赋值到特定字段来更改值。下面代码展示了如何更改可变 User 实例的 email 字段中的值。 fn main() {let mut user1 User {active: true,username: String::from(someusername123),sign_in_count: 1,email: String::from(someoneexample.com),};println!(user:{}s email:{}, user1.username, user1.email);user1.email String::from(anotheremailexaple.com);println!(now user:{}s email:{}, user1.username, user1.email); }struct User {active: bool,username: String,email: String,sign_in_count: u64, } 请注意整个实例必须是可变的Rust 不允许我们只将某些字段标记为可变。与任何表达式一样我们可以在函数体的最后一个表达式中构造一个新的结构体实例从而隐式返回该新实例。 下面代码显示了一个 build_user 函数该函数用给定的电子邮件和用户名返回一个 User 实例。 active 字段的值为 true 而 sign_in_count 的值为 1 。 fn build_user(email: String, username: String) - User {User {active: true,username: username,email: email,sign_in_count: 1,} }将函数参数命名为与结构体字段相同的名称是合理的但必须重复 email 和 username 字段名称和变量有点乏味。如果结构体有更多的字段重复每个字段的名称就会更加烦人。幸运的是有一种简便的方法 1.1 使用字段初始化速记 上面build_user代码由于参数名和结构体字段名完全相同我们可以使用字段初始化速记语法重写 build_user 使其行为完全相同但没有 username 和 email 的重复代码如下所示 fn build_user(email: String, username: String) - User {User {active: true,username,email,sign_in_count: 1,} }在这里我们要创建一个 User 结构的新实例它有一个名为 email 的字段。我们要将 email 字段的值设置为 build_user 函数的 email 参数中的值。由于 email 字段和 email 参数的名称相同我们只需写入 email 而不是 email: email 。 1.2 使用结构更新语法从其他实例创建实例  创建一个结构体的新实例其中包含另一个实例中的大部分值但要更改其中的一些值这通常很有用。您可以使用 struct update 语法来实现这一功能。 首先下列代码展示了如何在 user2 中定期创建一个新的 User 实例而不使用更新语法。我们为 email 设置了一个新值但使用了之前代码中创建的 user1 中的相同值。 fn main() {let mut user1 User {active: true,username: String::from(someusername123),sign_in_count: 1,email: String::from(someoneexample.com),};let user2 User {active: user1.active,username: user1.username,email: String::from(anotherexample.com),sign_in_count: user1.sign_in_count,}; }struct User {active: bool,username: String,email: String,sign_in_count: u64, } 如下代码所示使用结构体更新语法我们可以用较少的代码实现相同的效果。语法 .. 规定未明确设置的其余字段应与给定实例中的字段具有相同的值。 fn main() {let mut user1 User {active: true,username: String::from(someusername123),sign_in_count: 1,email: String::from(someoneexample.com),};let user2 User {email: String::from(anotherexample.com),..user1}; }struct User {active: bool,username: String,email: String,sign_in_count: u64, } 上述代码还在 user2 中创建了一个实例该实例的 email 值不同但与 user1 中的 username 、 active 和 sign_in_count 字段的值相同。 ..user1 必须放在最后以指定其余字段应从 user1 中的相应字段获取值但我们可以选择以任意顺序为任意多个字段指定值与结构体定义中的字段顺序无关。 请注意结构更新语法像赋值一样使用 这是因为它移动了数据。在上述例中创建 user2 后我们不能再将 user1 作为一个整体来使用因为 user1 的 username 字段中的 String 被移动到了 user2 中。如果我们为 user2 的 email 和 username 都赋予新的 String 值从而只使用 user1 中的 active 和 sign_in_count 值那么 user1 在创建 user2 后仍然有效。 active 和 sign_in_count 都是实现了 Copy 特性的类型。 1.3 使用无命名字段的元组结构创建不同类型 Rust 还支持与元组相似的结构体称为元组结构体。元组结构体具有结构体名称所提供的附加含义但没有与字段相关联的名称相反它们只有字段的类型。如果您想给整个元组一个名称并使元组与其他元组的类型不同而且用普通结构体命名每个字段会显得冗长或多余那么元组结构体就非常有用。 要定义元组结构首先要使用 struct 关键字和结构名然后是元组中的类型。例如我们在这里定义并使用了两个元组结构分别命名为 Color 和 Point struct Color(i32, i32, i32); struct Point(i32, i32, i32);fn main() {let black Color(0, 0, 0);let origin Point(0, 0, 0); } 请注意 black 和 origin 值的类型不同因为它们是不同元组结构的实例。您定义的每个结构体都有自己的类型即使结构体中的字段可能具有相同的类型。例如接收 Color 类型参数的函数不能接收 Point 作为参数尽管这两种类型都是由三个 i32 值组成的。除此之外元组结构实例与元组类似都可以将其重组为单独的部分并且可以使用后跟索引来访问单独的值。 1.4 无字段的类单元结构 您还可以定义没有任何字段的结构体这些结构体被称为类单元结构体unit-like structs因为它们的行为类似于我们在 元组类型 一节中提到的单元类型 () 。当你需要在某个类型上实现一个特质但又没有任何数据要存储在类型本身时类单元结构体就会派上用场。下面是一个名为 AlwaysEqual 的单元结构体的声明和实例化示例 struct AlwaysEqual;fn main() {let subject AlwaysEqual; } 定义 AlwaysEqual 时我们使用 struct 关键字、我们想要的名称然后使用分号。无需大括号或小括号然后我们可以用类似的方法在 subject 变量中获取 AlwaysEqual 的实例使用我们定义的名称不需要任何大括号或小括号。想象一下以后我们将为这种类型实现这样的行为 AlwaysEqual 的每个实例总是等于任何其他类型的每个实例也许这样做是为了测试目的。我们不需要任何数据来实现这种行为你将在第 10 章中看到如何定义 traits 并在任何类型上实现它们包括类单元结构体。 1.5 结构数据的所有权 在User 结构定义中我们使用了所拥有的 String 类型而不是 str 字符串片段类型。这是有意为之因为我们希望该结构的每个实例都拥有其所有数据并且只要整个结构有效这些数据就有效。 结构体也可以存储对其他数据的引用但这样做需要使用生命周期。生命周期可以确保结构体引用的数据在结构体存在期间一直有效。假设你试图在结构体中存储引用而不指定生命周期就像下面这样这是 fn main() {let mut user1 User {active: true,username: String::from(someusername123),sign_in_count: 1,email: String::from(someoneexample.com),}; }struct User {active: bool,username: String,email: String,sign_in_count: u64, } 编译器会抱怨说它需要指定生命周期 cargo.exe buildCompiling structType v0.1.0 (E:\rustProj\structType) error[E0106]: missing lifetime specifier-- src\main.rs:12:15| 12 | username: String,| ^ expected named lifetime parameter| help: consider introducing a named lifetime parameter | 10 ~ struct Usera { 11 | active: bool, 12 ~ username: a String,|error[E0106]: missing lifetime specifier-- src\main.rs:13:12| 13 | email: String,| ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 10 ~ struct Usera { 11 | active: bool, 12 | username: String, 13 ~ email: a String,|error[E0308]: mismatched types-- src\main.rs:4:19| 4 | username: String::from(someusername123),| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected String, found String| help: consider borrowing here| 4 | username: String::from(someusername123),| error[E0308]: mismatched types-- src\main.rs:6:16| 6 | email: String::from(someoneexample.com),| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected String, found String| help: consider borrowing here| 6 | email: String::from(someoneexample.com),| Some errors have detailed explanations: E0106, E0308. For more information about an error, try rustc --explain E0106. error: could not compile structType (bin structType) due to 4 previous errors 后面章节我们将讨论如何修复这些错误以便在结构体中存储引用但现在我们将使用自有类型如 String 而不是引用如 str 来修复类似错误。 2. 使用结构体的示例程序 为了了解什么情况下我们可能需要使用结构体让我们编写一个计算矩形面积的程序。我们先使用单变量然后重构程序直到使用结构体。 让我们用 Cargo 创建一个名为 rectangles 的新二进制项目它将以像素为单位指定矩形的宽和高并计算矩形的面积。下面代码展示了一个简短的程序其中的一种方法就是在我们项目的 src/main.rs 中进行计算。 fn main() {let width1 30;let height1 50;println!(The area of the rectangle is {} square pixels.,area(width1, height1)) }fn area(width: u32, height: u32) - u32 {width * height }现在使用 cargo run 运行该程序 cargo.exe runCompiling rectangles v0.1.0 (D:\rustProj\rectangles)Finished dev [unoptimized debuginfo] target(s) in 0.65sRunning target\debug\rectangles.exe The area of the rectangle is 1500 square pixels. 这段代码通过调用 area 函数成功地计算出了矩形的面积但我们还可以做得更多使代码更加清晰易读。 该代码的问题在 area 的签名中显而易见 fn area(width: u32, height: u32) - u32 { area 函数本应计算一个矩形的面积但我们编写的函数有两个参数而且程序中也没有明确说明这两个参数之间的关系。如果将宽度和高度组合在一起会更易于阅读和管理。在之前的章节中我们已经讨论过一种方法使用元组。 fn main() {let rect1 (30, 50);println!(The area of the rectangle is {} square pixels.,area(rect1)); }fn area(dimensions: (u32, u32)) - u32 {dimensions.0 * dimensions.1 }从某种意义上说这个程序更好。元组让我们增加了一些结构而且我们现在只传递一个参数。但从另一个角度看这个版本就不那么清晰了元组没有为其元素命名因此我们必须为元组的各个部分建立索引这使得我们的计算不那么明显。 混淆宽度和高度对计算面积没有影响但如果我们要在屏幕上绘制矩形就会有影响我们必须记住 width 是元组索引 0 height 是元组索引 1 。如果其他人使用我们的代码就更难理解和牢记这一点了。因为我们没有在代码中传达数据的含义所以现在更容易引入错误。 2.1 使用结构体进行重构增加更多意义 我们使用结构体通过标记数据来增加意义。如下所示我们可以将正在使用的元组转换为结构体并为整体和各部分命名。 struct RectAngle {width: u32,height: u32, }fn main() {let rect1 RectAngle {width: 30,height: 50,};println!(The area of the rectangle is {} square pixels.,area(rect1)); }fn area(rectAngle: RectAngle) - u32 {rectAngle.width * rectAngle.height }在这里我们定义了一个结构体并将其命名为 Rectangle 。在大括号中我们将字段定义为 width 和 height 这两个字段的类型都是 u32 。然后在 main 中我们创建了一个 Rectangle 的特定实例其宽度为 30 高度为 50 。 现在我们定义的 area 函数只有一个参数我们将其命名为 rectangle其类型是不可变的借用结构体 Rectangle实例。因为我们希望借用结构体而不是获取其所有权。这样 main 就可以保留其所有权并继续使用 rect1 这也是我们在函数签名和函数调用中使用 的原因。 area 函数访问 Rectangle 实例的 width 和 height 字段注意访问借用结构体实例的字段并不会移动字段值这就是为什么我们经常看到结构体的借用。现在我们对 area 的函数签名所表达的意思是使用 width 和 height 字段计算 Rectangle 的面积。这表达了宽度和高度是相互关联的并且为这些值提供了描述性的名称而不是使用 0 和 1 的元组索引值。这在清晰度方面是有优势的。 2.2 利用派生特质添加实用功能 如果能在调试程序时打印 Rectangle 的实例并查看其所有字段的值将非常有用。下面代码尝试使用 println! 宏就像我们在前几章中使用的那样。但这行不通。 struct Rectangle {width: u32,height: u32, }fn main() {let rect1 Rectangle {width: 30,height: 50,};println!(rect1 is {}, rect1); }fn area(rectangle: Rectangle) - u32 {rectangle.width * rectangle.height }当我们编译这段代码时会出现一个错误其核心信息是: error[E0277]: Rectangle doesnt implement std::fmt::Display println! 宏可以进行多种格式化默认情况下大括号会告诉 println! 使用 Display 供最终用户直接使用的输出格式。我们目前看到的基元类型默认使用 Display 因为只有一种方式可以向用户显示 1 或其他基元类型。但对于结构体 println! 的输出格式就不那么清晰了因为有更多的显示可能性要不要逗号要不要打印大括号是否要显示所有字段由于这种模糊性Rust 不会试图猜测我们想要什么而且结构体也没有提供 Display 的实现无法与 println! 和 {} 占位符一起使用。 如果我们继续阅读错误就会发现这条有用的说明 help: the trait std::fmt::Display is not implemented for Rectangle note: in format strings you may be able to use {:?} (or {:#?} for pretty-print) instead 让我们试试看 println! 宏调用现在看起来像 println!(rect1 is {:?}, rect1); 。在大括号中加上 :? 这个说明符可以告诉 println! 我们要使用一种名为 Debug 的输出格式。 Debug 特质使我们能够以一种对开发人员有用的方式打印结构体这样我们就可以在调试代码时看到结构体的值。 按此更改编译代码。糟糕还是出错 error[E0277]: Rectangle doesnt implement Debug 不过编译器还是给了我们一个有用的提示 help: the trait Debug is not implemented for Rectangle note: add #[derive(Debug)] to Rectangle or manually impl Debug for Rectangle Rust 确实包含了打印调试信息的功能但我们必须明确选择将该功能用于我们的结构体。为此我们在结构体定义之前添加外属性 #[derive(Debug)] 如下列代码所示 现在当我们运行程序时不会出现任何错误我们将看到以下输出 rect1 is Rectangle { width: 30, height: 50 } 不错这不是最漂亮的输出但它显示了该实例所有字段的值这对调试绝对有帮助。当我们使用较大的结构体时输出结果会更容易阅读在这种情况下我们可以在 println! 字符串中使用 {:#?} 代替 {:?} 。在本例中使用 {:#?} 样式将输出如下内容 rect1 is Rectangle {width: 30,height: 50, } 使用 Debug 格式打印数值的另一种方法是使用 dbg! 宏该宏获取表达式的所有权与 println! 相反后者获取引用打印 dbg! 宏调用在代码中出现的文件和行号以及表达式的结果值并返回数值的所有权。 注意调用 dbg! 宏将打印到标准错误控制台流 ( stderr )而不是 println! 后者将打印到标准输出控制台流 ( stdout ) 在下面的示例中我们关心的是分配给 width 字段的值以及 rect1 中整个结构体的值         #[derive(Debug)] struct Rectangle {width: u32,height: u32, }fn main() {let scale 2;let rect1 Rectangle {width: dbg!(30 * scale),height: 50,};dbg!(rect1); } 我们可以将 dbg! 放在表达式 30 * scale 的周围由于 dbg! 返回表达式值的所有权因此 width 字段将获得与没有调用 dbg! 时相同的值。我们不希望 dbg! 拥有 rect1 的所有权因此我们在下一次调用中使用了对 rect1 的引用。下面是这个示例的输出结果 cargo.exe runCompiling rectangles v0.1.0 (D:\rustProj\rectangles)Finished dev [unoptimized debuginfo] target(s) in 0.66sRunning target\debug\rectangles.exe [src\main.rs:10] 30 * scale 60 [src\main.rs:14] rect1 Rectangle {width: 60,height: 50, } 我们可以看到第一个输出来自 src/main.rs 第 10 行在这一行我们调试了表达式 30 * scale 其结果值是 60 Debug 对整数执行的格式是只打印其值。src/main.rs 第 14 行的 dbg! 调用会输出 rect1 的值即 Rectangle 结构。该输出使用了 Rectangle 类型的漂亮 Debug 格式。当您想弄清代码在做什么时 dbg! 宏确实很有帮助 除了 Debug 特质之外Rust 还提供了许多与 derive 属性一起使用的特质它们可以为我们的自定义类型添加有用的行为。 我们的 area 函数非常特殊它只计算矩形的面积。将这一行为与我们的 Rectangle 结构更紧密地联系起来会很有帮助因为它无法与任何其他类型一起工作。让我们看看如何通过将 area 函数转化为定义在 Rectangle 类型上的 area 方法来继续重构这段代码。 3. 方法语法 方法与函数类似我们使用 fn 关键字和名称来声明方法方法可以有参数和返回值方法中包含一些代码当从其他地方调用方法时这些代码将被运行。与函数不同的是方法是在结构体或枚举或特质对象的上下文中定义的其第一个参数始终是 self 表示方法被调用的结构体的实例。 3.1 定义方法 如下代码所示让我们改变以 Rectangle 实例为参数的 area 函数改用定义在 Rectangle 结构上的 area 方法。 #[derive(Debug)] struct Rectangle {width: u32,height: u32, }impl Rectangle {fn area(self) - u32 {self.width * self.height} }fn main() {let rect1 Rectangle {width: 30,height: 50,};println!(The area of the rectangle is {} square pixels.,rect1.area()); }为了在 Rectangle 的上下文中定义函数我们为 Rectangle 启动了一个 impl 实现块。 impl 块中的所有内容都将与 Rectangle 类型相关联。然后我们将 area 函数移入 impl 大括号中并将签名和正文中的第一个在本例中也是唯一的参数改为 self 。在 main 中我们调用了 area 函数并将 rect1 作为参数传递而在 Rectangle 实例中我们可以使用方法语法调用 area 方法。方法语法位于实例之后我们在方法名称前添加一个点。 在 area 的签名中我们使用 self 而不是 rectangle: Rectangle 。 self 实际上是 self: Self 的缩写。在 impl 代码块中类型 Self 是 impl 代码块的别名。方法的第一个参数必须是一个名为 self 类型为 Self 的参数因此 Rust 允许你在第一个参数位置只使用名称 self 来缩写它。请注意我们仍然需要在 self 速记前面使用 来表示该方法借用了 Self 实例就像我们在 rectangle: Rectangle 中做的那样。方法可以拥有 self 的所有权也可以不可变借用 self 就像我们在这里所做的或者可变借用 self 就像它们可以借用任何其他参数一样。 在这里我们选择 self 的原因与在函数版本中使用 Rectangle 的原因相同我们不想拥有所有权我们只想读取结构体中的数据而不是向其写入数据。如果我们想在调用方法的过程中改变调用方法的实例我们可以使用 mut self 作为第一个参数。仅使用 self 作为第一个参数来获取实例所有权的方法并不多见这种方法通常用于将 self 转换为其他内容并防止调用者在转换后使用原始实例。 使用方法而不是函数的主要原因除了提供方法语法和不必在每个方法的签名中重复 self 的类型外还在于组织。我们把所有能用类型实例做的事情都放在一个 impl 块中而不是让我们代码的未来用户在我们提供的库中的不同地方搜索 Rectangle 的功能。 请注意我们可以选择给方法起一个与结构体的某个字段相同的名字。例如我们可以在 Rectangle 上定义一个方法并将其命名为 width impl Rectangle {fn width(self) - bool {self.width 0} }fn main() {let rect1 Rectangle {width: 30,height: 50,};if rect1.width() {println!(The rectangle has a nonzero width; it is {}, rect1.width);} } 在这里我们选择让 width 方法在实例的 width 字段中的值大于 0 时返回 true 在 0 时返回 false 我们可以在同名的方法中使用字段来达到任何目的。在 main 中当我们在 rect1.width 后加上括号时Rust 知道我们指的是方法 width 。当我们不使用括号时Rust 知道我们指的是字段 width 。 通常但不总是当我们给一个方法起一个与字段相同的名字时我们希望它只返回字段中的值而不做其他事情。这样的方法称为获取器Rust 并不像其他语言那样自动为 struct 字段实现获取器。获取器之所以有用是因为我们可以将字段私有化但将方法公有化这样就可以将字段的只读访问作为类型的公有 API 的一部分。 - 操作符在哪里         在 C 和 C 中有两种不同的操作符用于调用方法如果是直接调用对象上的方法则使用 . 如果是调用对象指针上的方法则使用 - 并且需要先反向引用指针。换句话说如果 object 是一个指针那么 object-something() 与 (*object).something() 类似。         Rust 没有与 - 运算符等价的运算符相反Rust 有一种称为自动引用和取消引用的功能。在 Rust 中调用方法是少数几个具有这种行为的地方之一。         它是这样工作的当你使用 object.something() 调用一个方法时Rust 会自动添加 、 mut 或 * 这样 object 就与方法的签名相匹配了。换句话说下面的内容是一样的                 p1.distance(p2);                 (p1).distance(p2);         第一种方法看起来更简洁。这种自动引用行为之所以有效是因为方法有明确的接收者即self 的类型。有了方法的接收者和名称Rust 就能确定该方法是在读取 ( self )、变异 ( mut self )还是在消耗 ( self )。Rust 对方法接收者的借用是隐式的。 3.2 带有更多参数的方法 让我们通过在 Rectangle 结构上实现第二个方法来练习使用方法。这一次我们希望 Rectangle 的实例接收 Rectangle 的另一个实例如果第二个 Rectangle 可以完全容纳在 self 第一个 Rectangle 中则返回 true 否则返回 false 。也就是说一旦我们定义了 can_hold 方法我们就可以编写如下所示的程序。 fn main() {let rect1 Rectangle {width: 30,height: 50,};let rect2 Rectangle {width: 10,height: 40,};let rect3 Rectangle {width: 60,height: 45,};println!(Can rect1 hold rect2? {}, rect1.can_hold(rect2));println!(Can rect1 hold rect3? {}, rect1.can_hold(rect3)); } 预期输出结果如下因为 rect2 的两个尺寸都小于 rect1 的尺寸但 rect3 比 rect1 宽 Can rect1 hold rect2? true Can rect1 hold rect3? false我们知道要定义一个方法因此它将位于 impl Rectangle 代码块中。方法的名称是 can_hold 它将使用另一个 Rectangle 的不可变借用作为参数。通过观察调用该方法的代码我们可以知道参数的类型 rect1.can_hold(rect2) 将 rect2 传递给 rect2 后者是 Rectangle 的一个不可变借用实例。这是有道理的因为我们只需要读取 rect2 而不是写入这意味着我们需要一个可变借用而且我们希望 main 保留对 rect2 的所有权这样我们就可以在调用 can_hold 方法后再次使用它。 can_hold 的返回值将是一个布尔值其实现将检查 self 的宽度和高度是否分别大于其他 Rectangle 的宽度和高度。 让我们将新的 can_hold 方法添加到 impl 块如下所示 impl Rectangle {fn area(self) - u32 {self.width * self.height}fn can_hold(self, other: Rectangle) - bool {self.width other.width self.height other.height} }当我们使用 main 函数运行这段代码时我们将得到所需的输出结果。方法可以接受多个参数我们可以将这些参数添加到 self 参数之后的签名中这些参数的作用与函数中的参数相同。 3.3 相关函数 在 impl 代码块中定义的所有函数都称为关联函数因为它们与以 impl 命名的类型相关联。我们可以定义不以 self 作为第一个参数的关联函数因此不是方法因为它们不需要类型的实例来处理。我们已经使用过一个这样的函数定义在 String 类型上的 String::from 函数。 不是方法的关联函数通常用于返回结构体新实例的构造函数。这些函数通常被称为 new 但 new 并不是一个特殊的名称也没有内置在语言中。例如我们可以选择提供一个名为 square 的关联函数该函数只有一个维度参数并将其用作宽度和高度这样就可以更方便地创建一个正方形 Rectangle 而不必两次指定相同的值 impl Rectangle {fn square(size: u32) - Self {Self {width: size,height: size,}} }返回类型和函数体中的 Self 关键字是 impl 关键字后出现的类型的别名在本例中是 Rectangle 。 要调用这个关联函数我们使用 :: 句法并加上结构体名称 let sq Rectangle::square(3); 就是一个例子。该函数由结构体命名 :: 语法既用于关联函数也用于模块创建的命名空间。 3.4 多个 impl 块 每个结构体可以有多个 impl 块。例如下例中每个方法都有自己的 impl 块。 impl Rectangle {fn area(self) - u32 {self.width * self.height} }impl Rectangle {fn can_hold(self, other: Rectangle) - bool {self.width other.width self.height other.height} }这里没有理由将这些方法分隔成多个 impl 块但这是有效的语法。在后面章节中讨论泛型和特质时我们将看到多个 impl 块是有用的。 下一篇06-枚举和模式匹配

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/91875.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站与维护wordpress 删除超文本

rxjava 被观察者大约4年前,我第一次在Matthew Podwysocki的博客上遇到了Reactive Extensions ,但是直到我几周前看到Matthew在Code Mesh上发表演讲后,我才对它有所了解。 最近它似乎越来越流行,我注意到Netflix编写了一个Java版本…

这样制作公司网站科技企业网站如何建设

1.实验目的 掌握常见数据预处理方法,熟练运用数据分析方法,并掌握 Python 中的 Numpy、 Pandas 模块提供的数据分析方法。 2.实验内容 1. Pandas 基本数据处理 使用 Pandas 模块,完成以下操作。 (1)创建一个由 0 到 50…

厦门网站代理沈阳市住房和城乡建设厅网站

简单查询、复杂查询 简单查询: 单表 复杂查询: 1. 子查询(嵌套查询) 2. 连接查询 ************************连接查询**************************** 一.什么是连接查询 把多张表连接在一起,一块查 二.什么时候使用 数据来自于多张表的时候,要使用连接查询 三.连接查询的分类 1. …

织梦做的网站首页出现空白常用网站后台地址

Python 小白的课题报告—OpenCV 抠图项目实战(8) 本系列是 Python 小白的课题作业《基于OpenCV 的图像分割和抠图》。 需要说明的是,本系列并不能算是 OpenCV 的抠图项目教程,只是以此为主题的课题报告。其中包括了一个较为完整的…

数字火币交易网站开发网站如何做好seo

API开发应该是后端开发最常见的工作,而调试和测试API是非常关键的,这篇文章简单介绍几款常用的工具以供大家参考。 SoapUI SoapUI是很老牌的工具的,在之前Webservice盛行的时候经常会用到。 现在官方推出了Pro版本的ReadyAPI,但要…

深圳服装网站建设制作开发贵阳小程序开发软件公司

静态代理设计模式 代理设计模式最本质的特质:一个真实业务主题只完成核心操作,而所有与之辅助的功能都由代理类来完成。 例如,在进行数据库更新的过程之中,事务处理必须起作用,所以此时就可以编写代理设计模式来完成。…

哈尔滨模板做网站设计一套网站费用

大家好我是小峰,今天我们开始学习二叉树。 首先我们来学习什么是树? 树概念及结构 树是一种 非线性 的数据结构,它是由 n ( n>0 )个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的…

潍坊网站排名推广网页浏览器推荐

系列文章使用 abp cli 搭建项目给项目瘦身,让它跑起来完善与美化,Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API,包装返回模型再说Swagger,分组、描述、小绿锁接入GitHub,用JWT保护你的API异常处理和…

济宁高端网站建设wordpress为什么性能差

默认情况下,springboot 初始的项目中都有一个 application.yml 或者 application.properties 文件,如果我们希望再定义一个独立的配置文件用来配置特定业务数据,而不希望把这些配置内容都堆积在 application 配置文件中,实现这个需…

阿里巴巴网站怎么做全屏大图网站建设费用包括哪些内容

题目: 如图6-8所示,可以用四分树来表示一个黑白图像,方法是用根结点表示整幅图像,然后把行列个分城两等分,按照图中的方式编号,从左到右对应4个子结点。如果某子结点对应的取余全白或全黑,则直…

莉莉卡是哪个网站做的windows搭建网站开发

资料: 著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 作者:陈禹鲁 链接:http://www.zhihu.com/question/19809484/answer/35544452 来源:知乎 第一本,入门 《Head first HTML&…

网站建设制作设计推广优化初学者怎么制作平面图

高精度加法 1.题目2.基本思想3.代码实现4.总结 1.题目 给定两个正整数(不含前导 0),计算它们的和。 输入格式 共两行,每行包含一个整数。 输出格式 共一行,包含所求的和。 数据范围 1 ≤ 整数长度 ≤ 100000 1≤整…

网站备案添加域名邢台移动端网站建设

随着科技的飞速发展,人工智能(AI)已经从科幻小说中的概念变成了现实生活中的重要角色。AI大模型技术,作为人工智能领域的一项重要突破,如今已不再是一个新鲜词汇,而是正在深刻改变着我们的生活方式和工作模…

商城网站建设都有哪些类型大学高校网站建设栏目

目录 ACM金牌带你零基础直达C语言精通-课程资料 一.作用域的基本概念 二.函数 1. 函数的定义和使用 2.为什么一定要有函数结构 3.形参与实参 4.函数的声明和定义 5.递归函数 此代码中递归函数执行流程: 练习:求斐波那契数列第n项的值: 欧几里…

广州网站建设公司万齐网络科技html网站免费模板下载

本章概要 测试驱动开发 测试驱动 vs 测试优先 日志 日志信息日志等级 测试驱动开发 之所以可以有测试驱动开发(TDD)这种开发方式,是因为如果你在设计和编写代码时考虑到了测试,那么你不仅可以写出可测试性更好的代码&#xff…

网页设计素材图片黑白电脑优化系统的软件哪个好

java 泛型和类型擦除“编译期间擦除泛型”是常识&#xff08;好吧&#xff0c;类型参数和实参实际上是被擦除的&#xff09;。 这是由于“类型擦除”而发生的。 但这是错误的&#xff0c;正如许多开发人员所假设的那样&#xff0c;将<..>符号内指定的所有内容都删除了。 …

濮阳市网站怎么做宣传推荐做网站的话术

思科设备SSH登陆详细配置过程我们用GNS3进行拓扑搭建。实验拓扑图如下&#xff1a;进行完基本配置之后开始配置SSH服务器(R2)1.首先验证设备是否支持SSHR2#show ip ssh能够识别这条命令就说明支持。2.配置IP域名。使用config# ip domain-name domain-name全局配置模式命令配置网…

网站广告位有哪些网站二级目录怎么做

1. 类 1.1. 简介 TypeScript是面向对象的JavaScript。   类描述了所创建的对象共同的属性与方法。 1.2. 类的定义 class class_name { // 类作用域 }&#xff08;1&#xff09;定义类的关键字是class&#xff0c;后面紧跟类名&#xff0c;类可以包含以下几个模块&#xff…

光明新区城市建设局网站做网站要注册公司吗

1、考虑下列三种情况下&#xff0c;对比一下普通RNN的表现和LSTM和GRU表现&#xff1a; &#xff08;1&#xff09;早期观测值对预测未来观测者具有非常重要的意义。 考虑一个极端情况&#xff0c;其中第一个观测值包含一个校验和&#xff0c; 目标是在序列的末尾辨别校验和是…

自己有网站怎么推广wordpress网站设密码

线程组&#xff08; Threads &#xff08;Users&#xff09;&#xff09;理解&#xff1a;一个虚拟用户组&#xff0c;线程组内线程数量在运行过程中不会发生改变。 注意事项&#xff1a;线程间变量相互独立。 一个测试计划内可以包含多个线程组。 可定义内容&#xff1a; 取样…