【Rust】Rust学习 第五章使用结构体组织相关联的数据

news2024/11/23 15:20:13

5.1 定义结构体并实例化结构体

定义结构体,需要使用 struct 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 字段field)。

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,             // 最后多了一个,
}

实例化(不可变变量)

fn main() {

// 定义结构体
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

// 实例化
let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};
}

可变变量

fn main() {
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

// 可变变量,字段都可以修改
let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};

// 可以修改
user1.email = String::from("anotheremail@example.com");
}

字段初始化简写语法

fn main() {
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

// 字段初始化简写语法
fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}
}

通过已经存着的变量初始化新变量

fn main() {
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};

let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    active: user1.active,                           // 通过user1初始化
    sign_in_count: user1.sign_in_count,             // 通过user1初始化
};
}

简化

fn main() {
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};

let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1                               // 其余的值都通过user1创建
};
}

也可以定义与元组(在第三章讨论过)类似的结构体,称为 元组结构体tuple structs)。元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。

要定义元组结构体,以 struct 关键字和结构体名开头并后跟元组中的类型。

fn main() {
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}

注意 black 和 origin 值的类型不同,因为它们是不同的元组结构体的实例。你定义的每一个结构体有其自己的类型,即使结构体中的字段有着相同的类型。

也可以定义一个没有任何字段的结构体!它们被称为 类单元结构体unit-like structs)因为它们类似于 (),即 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。(后面章节介绍)

5.2 一个结构体的示例程序

使用 Cargo 新建一个叫做 rectangles 的二进制程序,它获取以像素为单位的长方形的宽度和高度,并计算出长方形的面积。

直接思维:

fn main() {
    let width = 32;
    let heigth = 25;
    println!("The area of the rectangle is {} square pixels.", area(width, heigth));

}

fn area(width : u32, heigth : u32) -> u32 {
    width * heigth
}

抽象成结构体:

struct Rectangle {
    width : u32,
    height : u32,
}


fn main() {
    let rectangle = Rectangle{ width : 30, height : 50};              // 这里width和height都必须写着
    println!("The area of the rectangle is {} square pixels.", area(&rectangle));

}

fn area(rectangle :&Rectangle) -> u32 {
    rectangle.height * rectangle.width
}

通过派生 trait 增加实用功能

一个很实用的功能,一步步来实现,直接println!打印看看

加上这些试试

接着加

5.3方法语法

 方法 与函数类似:它们使用 fn 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文,将分别在第六章和第十七章讲解),并且它们第一个参数总是 self,它代表调用该方法的结构体实例。

成员函数?

将上述案例中的函数用方法重写

#[derive(Debug)]
struct Rectangle {
    width : u32,
    height : u32,
}
impl Rectangle {
    fn area(&self) -> u32 {
        self.height * self.width
    }
}


fn main() {
    let rectangle = Rectangle{ width : 30, height : 50};              // 这里width和height都必须写着
    // println!("The area of the rectangle is {} square pixels.", area(&rectangle));
    println!("rectangle is {}", rectangle.area());

}

为了使函数定义于 Rectangle 的上下文中,我们开始了一个 impl 块(impl 是 implementation 的缩写)。接着将 area 函数移动到 impl 大括号中,并将签名中的第一个(在这里也是唯一一个)参数和函数体中其他地方的对应参数改成 self。然后在 main 中将我们先前调用 area 方法并传递 rect1 作为参数的地方,改成使用 方法语法method syntax)在 Rectangle 实例上调用 area 方法。方法语法获取一个实例并加上一个点号,后跟方法名、圆括号以及任何参数。

在 area 的签名中,使用 &self 来替代 rectangle: &Rectangle,因为该方法位于 impl Rectangle 上下文中所以 Rust 知道 self 的类型是 Rectangle。注意仍然需要在 self 前面加上 &,就像 &Rectangle 一样。方法可以选择获取 self 的所有权,或者像我们这里一样不可变地借用 self,或者可变地借用 self,就跟其他参数一样。

这里选择 &self 的理由跟在函数版本中使用 &Rectangle 是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 &mut self。通过仅仅使用 self 作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 self 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。

带有更多参数的方法

同其它语言

关联函数

impl 块的另一个有用的功能是:允许在 impl 块中定义 不 以 self 作为参数的函数。这被称为 关联函数(associated functions),因为它们与结构体相关联。它们仍是函数而不是方法,因为它们并不作用于一个结构体的实例。已经使用过 String::from 关联函数了。

静态函数?

#![allow(unused_variables)]
fn main() {
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 关联函数
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}
}

使用结构体名和 :: 语法来调用这个关联函数:比如 let sq = Rectangle::square(3);。这个方法位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。第七章会讲到模块。

多个 impl 块

总结

结构体让你可以创建出在你的领域中有意义的自定义类型。通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。方法允许为结构体实例指定行为,而关联函数将特定功能置于结构体的命名空间中并且无需一个实例。

参考:使用结构体来组织相关联的数据 - Rust 程序设计语言 简体中文版 (bootcss.com)

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

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

相关文章

从进程pid反推获得该进程所属容器

参考链接 https://cloud-atlas.readthedocs.io/zh_CN/latest/docker/debug/get_container_by_pid.html

基于Java+SpringBoot+Vue的生鲜交易系统设计与实现(源码+LW+部署文档等)

博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Idea使用Docker插件实现maven打包自动构建镜像

Docker 开启TCP 服务 vi /lib/systemd/system/docker.service改写以下内容 ExecStart/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock重启服务 #重新加载配置文件 systemctl daemon-reload #重启服务 systemctl restart docker.service此时docker已…

支付模块功能实现(小兔鲜儿)【Vue3】

支付 渲染基础数据 支付页有俩个关键数据,一个是要支付的钱数,一个是倒计时数据(超时不支付商品释放) 准备接口 import request from /utils/httpexport const getOrderAPI (id) > {return request({url: /member/order/$…

Redis实战案例27-UV统计

1. Redis的HyperLogLog的统计功能 示例: 表明HyperLogLog不管加入重复元素多少次都不会让count,不会计数重复元素,所以适合做UV计数 2. 简单实现UV测试 通过单元测试,向 HyperLogLog 中添加 100 万条数据,看看内存占…

小鼠是否使用分布性RL?DeepMind说是的

DeepMind的研究人员发现了大脑对多巴胺的反应与分布强化学习的趋势AI理论之间的相似之处。这些发现验证了分布强化学习的潜力,并促使DeepMind研究人员自豪地宣称“现在人工智能研究走在正确的道路上”。 在这项新研究中,来自DeepMind和哈佛大学的研究人…

C# 完成串口通信RS485

C# 完成串口通信RS485|RS232上下位机交互 第零步: 我用的是电脑usb 转串口的所以首先是驱动程序下载,我们用的是CH341 下载地址:https://www.wch.cn/downloads/CH341SER_EXE.html 第一步:连接机器 RS485 上面有三个端子&#xf…

LangChain手记 Overview

整理并翻译自DeepLearning.AILangChain的官方课程:Overview 综述(Overview) LangChain是为大模型应用开发设计的开源框架 LangChain目前提供Python和JavaScript(TypeScript)两种语言的包 LangChain的主攻方向是聚合和…

若依form中点击重置按钮,select2选中项不会被重置

若依form中点击重置按钮,select2选中项不会被重置问题,下面提供解决办法。 如图所示,点击重置按钮后, 值被重置为初始状态, 而select2仍然选中之前的选项。 解决办法 在 ry-ui.js文件中 625行增加 下边的代码即可 $("#" currentId).find(select).val…

Parquet存储的数据模型以及文件格式

文章目录 数据模型Parquet 的原子类型Parquet 的逻辑类型嵌套编码 Parquet文件格式 本文主要参考文献:Tom White. Hadoop权威指南. 第4版. 清华大学出版社, 2017.pages 363. Aapche Parquet是一种能有效存储嵌套数据的列式存储格式,在Spark中应用较多。 …

【框架篇】MyBatis 介绍及使用(详细教程)

一,MyBatis 介绍 MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO&#xff…

亚马逊积极布局金融科技业务,将在巴西推出信用卡服务

据外媒报道,近日亚马逊表示,其将与巴西布拉德斯科银行(Banco Bradesco)合作,在巴西推出信用卡服务。 Banco Bradesco执行长Octavio de Lazari Junior表示,双方合作的信用卡将于8月8日推出,该卡…

pinctrl_desc函数操作集

pinctrl_desc函数操作集 文章目录 pinctrl_desc函数操作集操作集原型struct pinctrl_opsstruct pinctrl_opsstruct pinconf_ops 操作集原型 pinctrl_desc结构体中包含下列函数操作集 /* 引脚控制操作的虚拟函数表,用于支持引脚分组等全局概念,这是可选的…

分布式异步任务组件(八)

分布式异步任务组件网络通信线程模型设计-- 大概说一下功能场景: 从节点和主节点建立连接,负责和主节点的网络IO通信,通信动作包括投票,心跳,举证等,步骤为读取主节点的信息,写入IO队列中&…

Kotlin基础(十一):反射和注解

前言 本文主要讲解kotlin反射和注解。 Kotlin文章列表 Kotlin文章列表: 点击此处跳转查看 目录 1.1 kotlin反射 1.1.1 kotlin反射概念和常见使用场景 在Kotlin中,反射是一种能够在运行时动态地获取、检查和操作类、属性、方法等结构的能力。Kotlin为反射提供了一…

vue新学习 05vue的创建运行原理

01.vue的创建过程 Vue的生命周期是指Vue实例从创建到销毁的整个过程中经历的一系列阶段。Vue的生命周期分为8个阶段,分别是: beforeCreate(创建前):在实例初始化之后,数据观测和事件配置之前被调用。此时…

后台管理系统中----菜单的展开和收起

1.下载vuex npm i vuex3.6.2 2.创建文件夹 src/store/index import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); import tab from "./tab"; export default new Vuex.Store({modules: {tab,}, });注册Vuex并且注册tab模块 来控…

Spring系列三:基于注解配置bean

文章目录 💗通过注解配置bean🍝基本介绍🍝快速入门🍝注意事项和细节 💗自己实现Spring注解配置Bean机制🍝需求说明🍝思路分析🍝注意事项和细节 💗自动装配 Autowired&…

RISC-V基础之内存分布与映射

内存映射是指将地址空间划分为不同的部分或段,每个段有不同的用途和属性。这段话介绍了五个段:文本段、全局数据段、动态数据段、异常处理器段和操作系统(OS)段。下面是对每个段的简要说明: 文本段:存放程序…

cookie、sessionStorage、localStorage的使用介绍,通过localStorage保存当前菜单、已打开菜单

这篇文章主要介绍前端常用的几种存储技术cookie、storage(sessionStorage、localStorage)的简单介绍和使用方法。 一、cookie 1、在JavaScript中使用cookie 使用原生的cookie // 设置cookie document.cookie "nameTom;age18;";// 获取coo…