013、方法

1. 方法和函数的区别

        方法与函数十分相似:它们都使用fn关键字及一个名称来进行声明;它们都可以拥有参数和返回值;另外,它们都包含了一段在调用时执行的代码

        但是,方法与函数依然是两个不同的概念,因为方法总是被定义在某个结构体(或者枚举类型trait对象我们会在后面分别介绍它们)的上下文中并且它们的第一个参数永远都是self用于指代调用该方法的结构体实例。 

2. 定义方法 

        现在,让我们把那个以 Rectangle 实例作为参数的 area 函数,改写为定义在 Rectangle 结构体中的 area 方法,如 示例5-13 所示。 

// 示例5-13:在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 的上下文环境中定义这个函数,我们需要将 area 函数移动到一个由 impl implementation)关键字❶起始的代码块中❷,并把签名中的第一个参数(也是唯一的那个参数)和函数中使用该参数的地方改写为 self

        除此之外,我们还需要把 main 函数中调用 area 函数的地方,用方法调用的语法进行改写。前者是将 rect1 作为参数传入 area 函数,而后者则直接在 Rectangle 实例上调用 area 方法❸。

        方法调用是通过在实例后面加点号,并跟上方法名、括号及可能的参数来实现的。由于方法的声明过程被放置在 impl Rectangle 块中,所以Rust能够将 self 的类型推导为 Rectangle

        也正是因为这样,我们才可以在 area 的签名中使用 &self 来代替 rectangle: &Rectangle。但我们依然需要在 self 之前添加 &,就像 &Rectangle 一样。

        方法可以在声明时选择获取 self 的所有权,也可以像本例一样采用不可变的借用 &self,或者采用可变的借用 &mut self。总之,就像是其他任何普通的参数一样。 

         在这里,选择 &self 签名的原因和之前选择使用 &Rectangle 的原因差不多:我们既不用获得数据的所有权也不需要写入数据,而只需要读取数据即可。

        假如我们想要在调用方法时改变实例的某些数据,那么就需要将第一个参数改写为 &mut self。通常来说,将第一个参数标记为 self 并在调用过程中取得实例的所有权的方法并不常见。

        这种技术有可能会被用于那些需要将 self 转换为其他类型,且在转换后想要阻止调用者访问原始实例的场景。使用方法替代函数不仅能够避免在每个方法的签名中重复编写 self 的类型,还有助于我们组织代码的结构。

        我们可以将某个类型的实例需要的功能放置在同一个 impl 块中,从而避免用户在代码库中盲目地自行搜索它们。

运算符->到哪里去了?

        在C和C++中调用方法时有两个不同的运算符:它们分别是直接用于对象本身的. 及用于对象指针的 ->

        之所以有这样的区别,是因为我们在调用指针的方法时首先需要对该指针进行解引用。换句话说,假如 object 是一个指针,那么 object->something() 的写法实际上等价于 (*object).something()

        虽然Rust没有提供类似的 -> 运算符,但作为替代,我们设计了一种名为自动引用和解引用的功能。方法调用是Rust中少数几个拥有这种行为的地方之一。

        它的工作模式如下:当你使用 object.something() 调用方法时,Rust会自动为调用者 object 添加 &&mut *,以使其能够符合方法的签名。换句话说,下面两种方法调用是等价的:

p1.distance(&p2);
(&p1).distance(&p2);

        第一种调用看上去要简捷得多。这种自动引用行为之所以能够行得通,是因为方法有一个明确的作用对象:self 的类型。

        在给出调用者和方法名的前提下,Rust可以准确地推导出方法是否是只读的(&self),是否需要修改数据(&mut self),是否会获取数据的所有权(self)。这种针对方法调用者的隐式借用在实践中可以让所有权系统更加友好且易于使用。

3. 带有更多参数的方法

        现在,让我们通过实现 Rectangle 结构体的第二个方法来继续练习使用这种方法。这次我们要实现的是:检测当前的 Rectangle 实例是否能完整包含传入的另外一个 Rectangle 实例,如果是的话就返回 true,否则返回 false

        也就是说,一旦我们完成了这个方法(can_hold),我们就能像 示例5-14 中所示的那样去使用它。 

// 示例5-14:使用还没有编写好的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,也就是指向 Rectangle 实例 rect2 的不可变借用。

        为了计算包容关系,我们只需要去读取 rect2 的数据(而不是写入,写入意味着需要一个可变借用)。main 函数还应该在调用 can_hold 方法后继续持有 rect2 的所有权,从而使得我们可以在随后的代码中继续使用这个变量。

        can_hold 方法在实现时会依次检查 self 的宽度和长度是否大于传入的 Rectangle 实例的宽度和长度,并返回一个布尔类型作为结果。现在,让我们在 示例5-13 里出现过的 impl 块中添加 can_hold 方法,如 示例5-15 所示。

/* 示例5-15:基于Rectangle实现can_hold方法,
该方法可以接收另外一个Rectangle作为参数 */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}
}

        当你将这段代码与 示例5-14 中的 main 函数合并运行后,就可以得到预期的输出结果。实际上,方法同样可以在 self 参数后增加签名来接收多个参数,就如同函数一样。 

 

4. 关联函数

        除了方法,impl 块还允许我们定义不用接收 self 作为参数的函数。由于这类函数与结构体相互关联,所以它们也被称为关联函数(associated function)。

        我们将其命名为函数而不是方法,是因为它们不会作用于某个具体的结构体实例。你曾经接触过的 String::from 就是关联函数的一种。

        关联函数常常被用作构造器来返回一个结构体的新实例。例如,我们可以编写一个接收一个维度参数的关联函数,它会将输入的参数同时用作长度与宽度来构造正方形的 Rectangle 实例:

impl Rectangle {fn square(size: u32) -> Rectangle {Rectangle { width: size, height: size }}
}

        我们可以在类型名称后添加 :: 来调用关联函数,就像 let sq = Rectangle:: square(3); 一样。这个函数位于结构体的命名空间中,这里的 :: 语法不仅被用于关联函数,还被用于模块创建的命名空间。我们后面再讨论此处的模块概念。 

5. 多个impl块

        每个结构体可以拥有多个 impl 块。例如,示例5-15 中的代码等价于 示例5-16 中的,下面的代码将方法放置到了不同的 impl 块中。 

// 示例5-16:使用多个impl块来重写示例5-15impl 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 块的必要,但它仍然是合法的。我们会在后面文章中讨论泛型和 trait 时看到多个 impl 块的实际应用场景。 

 

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

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

相关文章

如何压缩图片?电脑图片压缩的方法

如果图片体积过大不仅会占用过多的内存,还容易在平时处理和上传的时候被限制,传输起来也比较慢,这个时候最简单的方法就是通过专业的图片处理工具压缩图片大小,现如今有许多在线处理工具可以选择,今天分享的压缩图就是…

模型融合之模型堆叠

一、理论 模型堆叠(Model Stacking)是一种集成学习的方法,其本质是将多个基学习器(Individual Learner)的预测结果作为新的特征,再训练一个元学习器(Meta Learner)来进行最终的预测。…

【JUC的四大同步辅助类】

文章目录 一、CountDownLatch二、CyclicBarrier三、Semaphore四、Phaser 提示:以下是本篇文章正文内容,下面案例可供参考 一、CountDownLatch CountDownLatch如同火箭发射,计数只能不断减减,当到达0时即发射 场景示例&#xff1…

案例089:基于微信小程序的校园综合服务平台设计与实现

文末获取源码 开发语言:Java 框架:SSM JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序…

ALSA学习(5)——ASoC架构中的Machine

参考博客:https://blog.csdn.net/DroidPhone/article/details/7231605 (以下内容皆为原博客转载) 文章目录 一、注册Platform Device二、注册Platform Driver三、初始化入口soc_probe() 一、注册Platform Device ASoC把声卡注册为Platform …

Typora+PicGo+Gitee构建云存储图片

创建Gitee仓库 首先,打开工作台 - Gitee.com,自行注册一个账户 注册完后,新建一个仓库(记得仓库要开源) 然后创建完仓库后,鼠标移动到右上角头像位置,选择设置,并点击&#xff…

终于学会听英文歌了:A Sad Me In Your Eyes

A Sad Me In Your Eyes 来源: https://lyricstranslate.com/en/ln-party-sad-me-your-eyes-lyrics.html Fire can’t burn in my eyes If without your smile Snow can cover your smile If without your love When you think of me, I’ve gone too far I can’t …

了解OpenApi和Swagger

Swagger 和 OpenAPI 是一种用于描述 RESTful API 的规范和工具集合。在本文中,我们将探讨 Swagger 和 OpenAPI 的概念、作用、使用方法以及优缺点。 Swagger 和 OpenAPI 的概念 Swagger 是一种用于描述 RESTful API 的规范。它提供了一种简单的方式来描述 API 的请…

Python 热力图的绘制(Matplotlib篇-12)

Python 热力图的绘制(Matplotlib篇-12)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

文件内容搜索利器 - grep

文章目录 文件内容搜索利器 - grep语法默认无参数增加文件夹反向查找不区分大小写显示行数,精准定位更多信息文件内容搜索利器 - grep Linux grep 命令用于查找文件里符合条件的字符串。 官方定义为: grep, egrep, fgrep - print lines matching a pattern grep支持正则表达…

ubuntu安装conda

在 Ubuntu 系统上安装 Anaconda 是一个直接的过程。Anaconda 是一个流行的 Python 和 R 数据科学及机器学习平台,它包括了许多科学计算和数据处理的库。以下是在 Ubuntu 上安装 Anaconda 的步骤: 1. 下载 Anaconda 安装脚本 首先,需要从 An…

2024年年初Java5年实战面试题(北京)

高阶篇: 一、在面对千万条并发请求的情况下,如果数据库频繁查询导致崩溃,可以采取以下措施来解决问题: 1.缓存数据:可以使用缓存技术来减少对数据库的查询次数。将经常查询的数据存储在缓存中,例如使用Redis等内存数据库&#xff…

如何做好档案数字化前的鉴定工作

要做好档案数字化前的鉴定工作,可以按照以下步骤进行: 1. 确定鉴定目标:明确要鉴定的档案的内容、数量和性质,确定鉴定的范围和目标。 2. 进行档案清点:对档案进行全面清点和登记,包括数量、种类、状况等信…

立体匹配算法(Stereo correspondence)

SGM(Semi-Global Matching)原理: SGM的原理在wiki百科和matlab官网上有比较详细的解释: wiki matlab 如果想完全了解原理还是建议看原论文 paper(我就不看了,懒癌犯了。) 优质论文解读和代码实现 一位大神自己用c实现…

分析Java中的StringHelper类

目录 前言1. 概念2. 功能示例3. Demo示例 前言 在项目中实战学习并记录可用的工具类 1. 概念 Java标准库(java.lang包)并没有提供名为StringHelper的类。通常,类似的字符串处理工具类并不是Java标准库的一部分,而是由程序员自行…

python使用隐马尔可夫模型识别波形数据MFCC特征

python使用隐马尔可夫模型识别振动波形数据MFCC特征 1、简介 ​ 隐马尔可夫模型非常擅长对时间序列数据进行建模。 ​ 由于振动波形数据是时间序列信号,HMM能够满足波形分类需求。 ​ 隐马尔可夫模型是表示观察序列的概率分布的模型。假设输出是由隐藏状态生成的。 2、数…

如何在Windows安装Wnmp服务并实现固定地址远程访问

文章目录 前言1.Wnmp下载安装2.Wnmp设置3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 WNMP是Windows系统下的绿色NginxMysqlPHP环境集成套件包,安装完成后即可得到一个Nginx MyS…

58.0/PhotoShop 图层的应用(详细版)

目录 58.1 图层的概念 58.2 图层的控制面板 58.3 图层的基本操作 58.3.1 新建图层 58.3.2 选择图层 58.3.3 复制图层 58.3.4 调整图层的叠加顺序 58.3.5 合并图层 58.4 图层样式 58.4.1 投影 58.4.2 内阴影 58.4.3 外发光样式 58.4.4 内发光样式 58.4.5 斜面和浮雕…

JSONyaml和Properties

注:取自GPT,总是忘记了,那我干脆就写一篇blog YAML(YAML Ain’t Markup Language 或 YAML Ain’t a Markup Language)和 JSON(JavaScript Object Notation)是两种不同的数据序列化格式&#xf…

程序员提问的艺术:28.4K Star指南,告别成为办公室讨厌鬼!

Github: https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way 原文:http://www.catb.org/~esr/faqs/smart-questions.html ✅为什么讨厌某些提问者 未自行尝试解决问题: ❌“怎么用Java写一个排序算法?” 👍&#…