第五章 使用结构体来组织相关联的数据

结构,或者结构体,是一种自定义数据类型,它允许我们命名多个相关的值并将它们组成一个有机的结合体。

可以把结构体视作对象中的数据属性

1 对比元组和结构体之间的异同,并演示如何使用结构体

2 讨论如何定义方法和关联函数,他们可以指定那些与结构体数据相关的行为

结构体和枚举体是用来创建类型的基本工具,在特定领域中的新类型同样可以享受到Rust编译时类型检查系统的所有优势。

定义并实例化结构体

结构体需要给每个数据赋予名字,可以清楚地表明它们地意义。

由于字段命名,不再需要依赖顺序索引来指定或访问实例中的值。

关键字struct被用来定义并命名结构体,一个良好的结构体名称应当能反映出自身数据组合的意义。在随后的花括号中声明所有数据的名字及类型,这些数据被称为字段。

1 struct User {
2   username: String,
3 email: String,
4 sign_in_count: u64,
5 active: bool,
6 }

在为每个字段赋予具体的值来创建结构体实例,赋值的顺序不需要严格对应在结构体中声明它们的顺序。

结构体的定义就像类型的通用模版一样,当我们将具体的数据填入模板时就创建出了新的实例。

1 let user1 = User {
2 email: String::from("someone@example.con"),
3   username: String::from("someusername123"),
4 active: true,
5 sign_in_count: 1,
6 };

可以通过点号来访问实例中的特定字段。

1 let mut user1 = User {
2   email: String::from("someone@example.con"),
3 username: String::from("someusername123"),
4 active: true,
5 sign_in_count: 1,
6 };
7
8 user1.email = String::from("anothermail@example.com");

一旦实例可变,那么实例中的所有字段都将是可变的。

Rust不允许我们将单独声明某一部分字段的可变性。

1 fn build_user( email: String, username: String) -> User {
2   User { // 在变量名与字段名相同时使用简化版的字段初始化方法
3 // 参数与结构体字段拥有完全一致的名称,所以可以使用名为字段初始化简写的语法(field init shorthand)
4   email,
5 username,
6 active: true,
7 sign_in_count: 1,
8 }
9 }

使用结构体更新语法根据其他实例创建新实例

1 let user2 = User {
2   email: String::from("another@example.com"),
3 username: String::from("anotherusername455"),
4 ..user1 // .. 表明剩下的那些还未被显示赋值字段都与给定实例拥有相同的值
5 };

使用不需要对字段命名的元组结构体来创建不同的类型

元组结构体, 元组结构体同样拥有用于表明自身含义的名称,无须在声明它时对其字段进行命名,仅保留字段的类型即可。

一般来说,当你想要给元组赋予名字,并使其区别与其他拥有同样定义的元组时,可以使用元祖结构体。

1 struct Color(i32, i32, i32);
2 struct Point(i32, i32, i32);
3
4 let black = Color(0,0,0);
5 let origin = Point(0,0,0);

这里的 black ,origin 是不同的类型,因为它们两个分别是不同元组结构体的实例,

所以定义的每一个结构体都拥有自己的类型,即便结构体中的字段拥有完全相同的类型。 例如,一个以Color类型作为参数的函数不能合法地接收Point类型的参数,即使它们都是由3个i32组成的。

可以通过模式匹配解构为单独的部分,也可以通过.及索引来访问特定字段

没有任何字段的空结构体

单元结构体,没有任何字段的结构体()。也称为空结构体

当需要在某些类型实现一个trait,却不需要在该类型中存储任何数据时,空结构体可以发挥作用。

 1 //对于引用来说有生命周期的限制
2 struct User {
3   username: &str, // expected lifetime parameter
4 email: &str,
5 sign_in_count: u64,
6 active: bool,
7 }
8
9 fn main() {
10   let user1 = User {
11 email: "someone@example.com",
12 username: "someusername123",
13 active: true,
14 aign_in_count: 1,
15 };
16 }

一个结构体的示例程序

 1 fn main() {
2   let width1 = 30;
3   let height1 = 50;
4
5 println!("The area of the rectangel is {} aquare pixels.", area(width1, height1));
7 }
8 fn area(width: u32, height: u32) -> u32 {
9 width * height
10 }


元组重构

1 fn main() {
2   let rect1 = (30, 50);
3
4 println!("The area of the rectangel is {} aquare pixels.", area(rect1));
5 }
6
7 fn area(dimensions: (u32, u32)) -> u32 {
8 dimensions.0 * dimensions.1
9 }

使用结构体来重构代码,增加有意义的描述信息

 1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 fn main() {
7 let rect1 = Rectangle { width: 30, height: 50 };
8 println!("The area of the rectangel is {} aquare pixels.", area(&rect1));
9 }
10
11 fn area(rectange: &Rectangle) -> u32 { //不可变借用&Rectangle ,不会获得它的所有权
12 rectangle.width * rectangle.height
13 }

通过派生trait增加适用功能

 1 #[derive(Debug)]
2 struct Rectange {
3 width: u32,
4 height: u32,
5 }
6
7 fn main() {
8 let rect1 = Rectangle { width: 30, height: 50 };;
9 println!("rect1 is {:?}", rect1);
10 }

方法

方法总是被定义在某个结构体、枚举体或者triat对象,并且它们的第一个参数永远都是self,用于指代调用该方法的结构体实例。

 1 #[derive(Debug)]
2 struct Rectangle {
3 width: u32,
4 height: u32,
5 }
6
7 impl Rectangle {
8 fn area(&self) -> u32 { // self: &Self ---> Self is Rectangle
9 self.width * sellf.height
10 }
11 }
12
13 fn main() {
14 let rect1 = Rectangle { width: 30, height: 50 };
15 println!("The area of the rectangel is {} aquare pixels.", rect1.area());
16 }

为了在Rectangle的上下文环境中定义这个函数,需要将area函数移动到impl 关键字起始的代码块中,并把签名中的第一个参数(也是唯一的那个参数)和函数中使用该参数的地方改为self.

方法调用是通过实例后面加点好,并更上方法名、括号及可能的参数来实现。

带有更多参数的方法

 1 fn main() {
2 let rect1 = Rectangle { width: 30, height: 50 };
3 let rect2 = Rectangle { width: 10, height: 40 };
4 let rect3 = Rectangel { width: 60, height: 45 };
5
6 println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
7 println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
8 }
9
10 #[derive(Debug)]
11 struct Rectangle {
12 width: u32,
13 height: u32,
14 }
15
16 impl Rectangle {
17 fn area(&self) -> u32 { // self: &Self ---> Self is Rectangle
18 self.width * sellf.height
19 }
20
21 fn can_hold(&self, other: &Rectangle) -> bool {
22 self.width > other.width && self.height > other.height
23 }
24 }
25
26 // 关联函数
27 impl Rectangle {
28 fn squaer(size: u32) -> Rectange {
29 Rectange { width: size, height: size }
30 }
31 }
关联函数常常用作构造器来返回一个结构体的新实例。

 类型名称后面添加::来调用关联函数

 

 :: 语法不仅被用于关联函数,还被用于模块创建的明命名空间。

多个imple块

 
 1 impl Rectangle {
2 fn area(&self) -> u32 { // self: &Self ---> Self is Rectangle
3 self.width * sellf.height
4 }
5 }
6
7 impl Rectangle {
8 fn can_hold(&self, other: &Rectangle) -> bool {
9 self.width > other.width && self.height > other.height
10 }
11 }
 

【Rust-book】第五章 使用结构体来组织相关联的数据的更多相关文章

  1. python 教程 第十五章、 结构布局

    第十五章. 结构布局 #!/usr/bin/env python #(1)起始行 "this is a module" #(2)模块文档 import sys #(3)模块导入 d ...

  2. 全国计算机等级考试二级教程-C语言程序设计_第14章_结构体、共用体和用户定义类型

    函数的返回值是结构体类型 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> struct ...

  3. GO开发[五]:golang结构体struct

    Go结构体struct Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性 ...

  4. 深入浅出ExtJS 第五章 树形结构

    5.1 TreePanel的基本使用 //树是一种非常典型的数据结构; 5.1.1 创建一棵树 //树控件有Ext.tree.TreePanel类定义,控件的名称为TreePanel;TreePane ...

  5. 【blockly教程】第五章 循环结构

    在这里,我们将介绍一个新游戏--Pond Tutor 在Pond Tutor(https://blockly-games.appspot.com/pond-tutor)这个游戏中,我们将扮演黄色的鸭子 ...

  6. C语言 第五章 循环结构练习

    一.计算15+16+17 …98+99的和 for实现 #include "stdio.h" void main() { //15+16+17 …98+99 ; ;i<=;i ...

  7. C语言 第五章 循环结构

    一.for 请在屏幕上输出1000个*号 printf("*************************...."); #include "stdio.h" ...

  8. Java 第五章 循环结构1

    循环结构 1 while 循环结构 ,do- while 循环结构 . 循环结构: 必须满足两个条件 . 1,循环条件 和 循环 操作 ! while 循环 特点:先判断,再执行 , 编码规范:缩进, ...

  9. C 语言入门第五章--循环结构和选择结构

    C语言中有三大结构,分别是顺序结构.选择结构和循环结构: 逻辑运算: 与运算: && 或运算:|| 非运算:! ==== #include<stdio.h> int mai ...

  10. rust结构体

    //Rust 并不允许只将某个字段标记为可变 struct User { email: String, name:String, age:i32, sex:String, active:bool, } ...

随机推荐

  1. 如何单机部署多个 MySQL 8.0 实例 ?

    在服务器资源有限的情况下,可利用该方案快速搭建各类 mysql 架构方案.各 MySQL 实例共享一个 mysqld 主程序,但各实例数据目录是独立的,存放在不同的文件夹中:好了.废话不多说,直接上干 ...

  2. EasyRelation发布,简单强大的数据关联框架

    当开发人员需要进行关联查询时,往往需要编写大量的冗余代码来处理数据之间的关系.这不仅浪费时间和精力,还会影响项目的可维护性和可扩展性. EasyRelation 是一个简单.高效的自动关联数据框架,可 ...

  3. axios和后端交互时,参数需要写在body和query中同时写

    axios.post('/api/xxx',{ // post body },{ params: { // query } }) demo: let params = { _id:this.alarm ...

  4. [小迪安全]笔记 day12、13 MySQL注入

    1. 简单案例 1.1 简易代码分析SQL注入原理 http://localhost:8085/sqli-labs/Less-2/index.php?id=2 id=2 正常查询 http://loc ...

  5. LeeCode 92双周赛复盘

    T1: 分割圆的最少切割次数 思维题: n 为偶数时,可以对半切割,切割 \(\frac{n}{2}\)次即可 n 为奇数时,不满足对称性,需要切割 n 次 n 为 1 时,不需要切割 public ...

  6. sql 时间函数

    计算时间间隔 day datediff(大日期, 小日期) SELECT datediff('2009-07-31', '2009-07-30') month, year, second timest ...

  7. KK 与答辩

    KK 与答辩 解读一下题:如果在所有场的答辩中,有某个人的总分都要低于kk的总分,就说kk碾压该人 --> 如果在某场答辩中这个人的总分大于kk,那么就说明kk不能碾压该人. 思路就清晰了,我们 ...

  8. Solidity 入门

    基本语法 版本指令 所有Solidity源码都必须指明版本,用于标明Solidity编译器的版本,这是为了避免将来新的编译器破坏代码 pragma solidity ^0.4.20; // 声明版本 ...

  9. odoo 开发入门教程系列-QWeb简史

    QWeb简史 到目前为止,我们的房地产模块的界面设计相当有限.构建列表视图很简单,因为只需要字段列表.表单视图也是如此:尽管使用了一些标记,如<group>或<page>,但在 ...

  10. 如何通过C#/VB.NET 代码调整PDF文档的页边距

    PDF边距是页面主要内容区域和页面边缘之间的距离.与Word页边距不同,PDF文档的页边距很难更改.因为Adobe没有提供操作页边距的直接方法.但是,您可以通过缩放页面内容来改变页边距.本文将介绍如何 ...