rust学习——方法 Method
文章目录
- 方法 Method
- 定义方法
- self、&self 和 &mut self
- 方法名跟结构体字段名相同
- 带有多个参数的方法
- 关联函数
- 多个 impl 定义
- 为枚举实现方法
- rust 结构体与枚举的区别
- 回答1
- 回答2
方法 Method

从面向对象语言过来的同学对于方法肯定不陌生,class 里面就充斥着方法的概念。在 Rust 中,方法的概念也大差不差,往往和对象成对出现:
object.method()
例如读取一个文件写入缓冲区,如果用函数的写法 read(f, buffer),用方法的写法 f.read(buffer)。不过与其它语言 class 跟方法的联动使用不同(这里可能要修改下),Rust 的方法往往跟结构体、枚举、特征(Trait)一起使用,特征将在后面几章进行介绍。
定义方法
Rust 使用 impl 来定义方法,例如以下代码:
struct Circle {x: f64,y: f64,radius: f64,
}impl Circle {// new是Circle的关联函数,因为它的第一个参数不是self,且new并不是关键字// 这种方法往往用于初始化当前结构体的实例fn new(x: f64, y: f64, radius: f64) -> Circle {Circle {x: x,y: y,radius: radius,}}// Circle的方法,&self表示借用当前的Circle结构体fn area(&self) -> f64 {std::f64::consts::PI * (self.radius * self.radius)}
}
我们这里先不详细展开讲解,只是先建立对方法定义的大致印象。下面的图片将 Rust 方法定义与其它语言的方法定义做了对比:

可以看出,其它语言中所有定义都在 class 中,但是 Rust 的对象定义和方法定义是分离的,这种数据和使用分离的方式,会给予使用者极高的灵活度。
再来看一个例子:
#[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 Rectangle {} 表示为 Rectangle 实现方法(impl 是实现 implementation 的缩写),这样的写法表明 impl 语句块中的一切都是跟 Rectangle 相关联的。
self、&self 和 &mut self

接下来的内容非常重要,请大家仔细看。在 area 的签名中,我们使用 &self 替代 rectangle: &Rectangle,&self 其实是 self: &Self 的简写(注意大小写)。在一个 impl 块内,Self 指代被实现方法的结构体类型,self 指代此类型的实例,换句话说,self 指代的是 Rectangle 结构体实例,这样的写法会让我们的代码简洁很多,而且非常便于理解:我们为哪个结构体实现方法,那么 self 就是指代哪个结构体的实例。
需要注意的是,self 依然有所有权的概念:
self表示Rectangle的所有权转移到该方法中,这种形式用的较少&self表示该方法对Rectangle的不可变借用&mut self表示可变借用
总之,self 的使用就跟函数参数一样,要严格遵守 Rust 的所有权规则。
回到上面的例子中,选择 &self 的理由跟在函数中使用 &Rectangle 是相同的:我们并不想获取所有权,也无需去改变它,只是希望能够读取结构体中的数据。如果想要在方法中去改变当前的结构体,需要将第一个参数改为 &mut self。仅仅通过使用 self 作为第一个参数来使方法获取实例的所有权是很少见的,这种使用方式往往用于把当前的对象转成另外一个对象时使用,转换完后,就不再关注之前的对象,且可以防止对之前对象的误调用。
简单总结下,使用方法代替函数有以下好处:
- 不用在函数签名中重复书写
self对应的类型 - 代码的组织性和内聚性更强,对于代码维护和阅读来说,好处巨大
方法名跟结构体字段名相同
在 Rust 中,允许方法名跟结构体的字段名相同:
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);}
}
当我们使用 rect1.width() 时,Rust 知道我们调用的是它的方法,如果使用 rect1.width,则是访问它的字段。
一般来说,方法跟字段同名,往往适用于实现 getter 访问器,例如:
pub struct Rectangle {width: u32,height: u32,
}impl Rectangle {pub fn new(width: u32, height: u32) -> Self {Rectangle { width, height }}pub fn width(&self) -> u32 {return self.width;}
}fn main() {let rect1 = Rectangle::new(30, 50);println!("{}", rect1.width());
}
用这种方式,我们可以把 Rectangle 的字段设置为私有属性,只需把它的 new 和 width 方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器 rect1.width() 方法来获取矩形的宽度,因为 width 字段是私有的,当用户访问 rect1.width 字段时,就会报错。注意在此例中,Self 指代的就是被实现方法的结构体 Rectangle。
-> 运算符到哪去了?
在 C/C++ 语言中,有两个不同的运算符来调用方法:
.直接在对象上调用方法,而->在一个对象的指针上调用方法,这时需要先解引用指针。换句话说,如果object是一个指针,那么object->something()和(*object).something()是一样的。Rust 并没有一个与
->等效的运算符;相反,Rust 有一个叫 自动引用和解引用的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。他是这样工作的:当使用
object.something()调用方法时,Rust 会自动为object添加&、&mut或*以便使object与方法签名匹配。也就是说,这些代码是等价的:p1.distance(&p2); (&p1).distance(&p2);第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者————
self的类型。在给出接收者和方法名的前提下,Rust 可以明确地计算出方法是仅仅读取(&self),做出修改(&mut self)或者是获取所有权(self)。事实上,Rust 对方法接收者的隐式借用让所有权在实践中更友好。
带有多个参数的方法
方法和函数一样,可以使用多个参数:
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}
}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));
}
关联函数
现在大家可以思考一个问题,如何为一个结构体定义一个构造器方法?也就是接受几个参数,然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了,很简单,参数中不包含 self 即可。
这种定义在 impl 中且没有 self 的函数被称之为关联函数: 因为它没有 self,不能用 f.read() 的形式调用,因此它是一个函数而不是方法,它又在 impl 中,与结构体紧密关联,因此称为关联函数。
在之前的代码中,我们已经多次使用过关联函数,例如 String::from,用于创建一个动态字符串。
impl Rectangle {fn new(w: u32, h: u32) -> Rectangle {Rectangle { width: w, height: h }}
}
Rust 中有一个约定俗成的规则,使用
new来作为构造器的名称,出于设计上的考虑,Rust 特地没有用new作为关键字。
因为是函数,所以不能用 . 的方式来调用,我们需要用 :: 来调用,例如 let sq = Rectangle::new(3, 3);。这个方法位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。
多个 impl 定义
Rust 允许我们为一个结构体定义多个 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 块,这里只是为了演示方便。
为枚举实现方法
枚举类型之所以强大,不仅仅在于它好用、可以同一化类型,还在于,我们可以像结构体一样,为枚举实现方法:
#![allow(unused)]
enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32),
}impl Message {fn call(&self) {// 在这里定义方法体}
}fn main() {let m = Message::Write(String::from("hello"));m.call();
}
除了结构体和枚举,我们还能为特征(trait)实现方法,这将在下一章进行讲解,在此之前,先来看看泛型。
rust 结构体与枚举的区别
枚举都有方法了,那么rust 结构体与枚举有什么区别呢
回答1
使用上,枚举定义其中一项就可以使用了,每一项之间是相对立的。而结构体主要是为了将有联系的数据放在一起,便于将数据归类。
枚举是一个萝卜一个坑,但结构体是一块地。
回答2
第一:枚举是列举所有可能的成员 ,而结构体是一个多种类型的数据组合;
第二:实例化结构体,必须为每个成员指定一个具体的值,而枚举的成员是各种数据类型,包括字符串、数值、结构体甚至另一个枚举,通过模式匹配具体的指令
当枚举的成员是非结构体类型时,自然清晰了
引用知乎网友回答
相关文章:
rust学习——方法 Method
文章目录 方法 Method定义方法self、&self 和 &mut self方法名跟结构体字段名相同 带有多个参数的方法关联函数多个 impl 定义为枚举实现方法 rust 结构体与枚举的区别回答1回答2 方法 Method 从面向对象语言过来的同学对于方法肯定不陌生,class 里面就充斥…...
目录遍历漏洞
漏洞挖掘之目录遍历漏洞 (baidu.com) 从0到1完全掌握目录遍历漏洞 0x01 什么是目录遍历漏洞 目录遍历漏洞是由于网站存在配置缺陷,导致网站目录可以被任意浏览,这会导致网站很多隐私文件与目录泄露。 比如数据库备份文件、配置文件等,攻击…...
Python基础入门例程10-NP10 牛牛最好的朋友们
目录 描述 输入描述: 输出描述: 示例1 解答: 说明: 描述 牛牛有两个最好的朋友,他们的名字分别用input读入记录在两个字符串中,请使用字符串连接()帮助牛牛将两个朋友的名字依…...
html web前端,登录,post请求提交 json带参
html web前端,登录,post请求提交 json带参 3ca9855b3fd279fa17d46f01dc652030.jpg <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><title></t…...
防火墙实验
防火墙 ping(网络测试工具,测试主机之间的可达性)原理: 发送一些小的网络数据包(ICMP数据包)到目标主机,并等待目标主机返回一个响应(通常是回显应答 Echo Reply)。 ss…...
php中进程、线程、协程详细讲解
目录 一、什么是进程、线程、协程 1、什么是进程(Process): 2 、什么是线程(Thread): 3、什么是协程(Coroutine): 二、 进程、线程、协程的关系 1、进程与线程关系 …...
无线通信中SINR的含义
在无线通信中,SINR代表"Signal-to-Interference-plus-Noise Ratio",即信号与干扰加噪声比。这是一个重要的性能度量,用于评估和描述接收信号的质量,以及在无线通信系统中的通信性能。 SINR考虑了以下三个关键因素&…...
pnp单目相机标定测距
参考:opencv 单目相机pnp测距(Cpp)-CSDN博客...
Java反射获取内部类方法
Java反射获取内部类方法 结论一、案例准备二、测试方法:使用反射获取类的成员内部类和方法具体操作具体操作(使用getDeclaredClasses) 结论 Java 通过反射可以获得内部类,包括内部类属性信息和方法。 一、案例准备 创建了一个类…...
发挥服务器的无限潜能:创意项目、在线社区和更多
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 ✨特色专栏:…...
华为OD 绘图机器(100分)【java】A卷+B卷
华为OD统一考试A卷+B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应20022部分考题以及新出的题目,A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载,请点击以下链接进入: 我用夸克网盘分享了「华为O…...
文件上传接口
以下是一个简单的Java文件上传接口的示例代码: import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.Requ…...
基于矢量控制策略的异步电机调速系统设计
摘 要 由于国内人民生活水平的提高,科技不断地进步,控制不断地完善,从而促使矢量控制技术在电气传动系统领域占据主导权,也使得交流异步电机调速控制系统被广泛应用。在交流异步电机调速系统设计领域中,矢量控制成为目…...
Ubuntu下载工具ip addr、ifconfig、ping、make
Ubuntu下载工具ip addr、ifconfig、ping、make ping 在 Ubuntu 上获取网络工具包通常是通过安装相关软件包的方式来完成的。Ubuntu 默认包含一些常见的网络工具,但如果你需要安装其他工具,你可以使用 apt 命令或者 snap 命令进行安装。以下是一些常见的…...
【数据结构】常见复杂度习题详解 ------ 习题篇
文章目录 📋前言一. ⛳️前篇回顾二. ⛳️常见时间复杂度计算举例1️⃣实例一2️⃣实例二3️⃣实例三4️⃣实例四5️⃣实例五6️⃣实例六7️⃣实例七8️⃣实例八 三. ⛳️常见空间复杂度计算举例1️⃣实例一2️⃣实例二3️⃣实例三 四. ⛳️总结 📋前言 …...
一、vue介绍
一、介绍 vue式前端框架,是一套用于构建用户界面的渐进式框架 1、安装vue 安装node.js(配置环境变量) https://nodejs.org/en/download/ 更换镜像 npm config set registry https://registry.npm.taobao.org 查看镜像 npm config get regi…...
Linux ARMv8 异常向量表
http://blog.chinaunix.net/uid-69947851-id-5830546.html 本章接着《Linux内核启动》部分讲解,我们知道了在进入start_kernel之前,通过指令adr_l x8, vectors;msr vbar_el1, x8设置了异常向量表,那么异常向量表的结构是怎么样…...
C++基类和派生类的内存分配,多态的实现
目录 基类和派生类的内存分配基类和派生类的成员归属多态的实现 基类和派生类的内存分配 类包括成员变量(data member)和成员函数(member function)。 成员变量分为静态数据(static data)和非静态数据&…...
C/C++基础
C 二进制 问题:二进制怎么表示整数、小数、正数、负数,如何存储?加减乘除怎么运算(见文章《计算机加减乘除本质》)? 变量 c定义一个变量的时候,需要事先定义变量大小和变量类型。 //有符号…...
MySQL基础练习题
数据表介绍 --1.学生表 Student(SId,Sname,Sage,Ssex) --SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 --2.课程表 Course(CId,Cname,TId) --CId 课程编号,Cname 课程名称,TId 教师编号 --3.教师表 Teacher(TId,Tname) --TId 教师编号,Tname 教师姓名 --4.成绩…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
