当前位置: 首页 > news >正文

【Rust自学】5.3. struct的方法(Method)

5.3 struct的方法(Method)

5.3.1. 什么是方法(Method)

方法和函数类似,也是用fn关键字进行声明,方法也有名称、参数和返回值。但方法和函数也有不同之处:
- 方法在struct(或枚举或 trait 对象)的上下文中定义。
- 方法的第一个参数总是self,表示方法所属(被调用)的struct实例,类似于 Python 中的self和 JavaScript 中的this

5.3.2. 方法的实际应用

接下来还是看例子,以上一篇文章 5.2. struct使用例(加打印调试信息) 的代码为例:

struct Rectangle { width: u32, length: u32, } fn main() { let rectangle = Rectangle{ width: 30, length: 50, }; println!("{}", area(&rectangle)); } fn area(dim:&Rectangle) -> u32 { dim.width * dim.length }

area这个函数的作用是计算面积,但它很特别:它只适用于矩形,而不适用于其他形状或其他类型。如果后面要加上计算其他图形面积的函数,那么area这个名字就会变得含糊。如果改名成rectangle_area又太麻烦,因为main里所有调用这个函数的地方也都要改。

所以如果能把存储矩形长宽的Rectangle结构体,和只能计算矩形面积的area函数结合到一起,就是最好的。

对于这种需求,Rust 提供了“实现”(implementation),其关键字是impl。后边跟着 struct 名,加上一对{},在里面像定义普通函数一样定义方法就行。

对于这个例子,struct 名就是Rectangle,把定义area函数的代码剪贴到{}内即可:

impl Rectangle { fn area(dim:&Rectangle) -> u32 { dim.width * dim.length } }

但注意这里的代码还不是方法,因为方法的第一个参数必须是self。现在的代码叫关联函数,下文会讲。

这么写是没有问题的,但还可以进一步简化。上文中说到了方法的第一个参数总是self,所以这里也可以改一下:

impl Rectangle { fn area(&self) -> u32 { self.width * self.length } }

你当前写的这个方法绑定在谁上,self指的就是谁。这个代码中area函数被绑定在Rectangle上,所以self就指的是Rectanglearea的参数不用拿走所有权,所以在self前面加上&表示引用。

当然这么改之后,main函数里的函数调用也要改——从函数调用改到方法调用:实例.方法名(参数)

fn main() { let rectangle = Rectangle{ width: 30, length: 50, }; println!("{}", rectangle.area()); }

rectangle.area()的括号中不写东西,是因为area方法在定义时只使用了&self作为参数,表示这个方法借用了self(即rectangle实例)的不可变引用。在调用area时,你不需要显式地传递这个实例,因为方法调用已经隐式地知道selfrectangle

整体代码如下:

struct Rectangle { width: u32, length: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.length } } fn main() { let rectangle = Rectangle{ width: 30, length: 50, }; println!("{}", rectangle.area()); }

输出:

1500

5.3.3. 如何定义方法

在上面的实际应用中已经写过一遍了,所以这里就只做总结:
- 在impl里定义方法
- 方法的第一个参数可以是self&self&mut self。可以获得所有权、不可变引用或可变引用,这点和其他参数一样。
- 方法可以帮助更好地组织代码,因为可以把某个类型的方法都放在同一个impl块里面,这样就不必在整个代码库里搜索与某个struct相关的行为了。

5.3.4. 方法调用的运算符

在 C/C++ 中,调用方法有两种运算符:
-->:其格式为object->something()。调用指针指向的对象上的方法就使用这一种(也就是object为指针时)。
-.:其格式为object.something()。调用对象本身上的方法就使用这种(也就是object不为指针,是个对象时)。

object->something()实际上是语法糖,它等同于(*object).something()*表示解引用。两者的流程都是先解引用,得到对象,再在对象上调用方法。

Rust 提供了自动引用/解引用的特性。也就是说,在调用方法时,Rust 会根据情况自动添加&&mut*,以便object可以匹配方法的签名。这点和 Go 语言一样。

举个例子,下面这两行代码效果相同:

point1.distance(&point2); (&point1).distance(&point2);

Rust 会根据情况自动在point1前加上&

5.3.5. 方法的参数

方法除了self也可以带其他参数,一个或多个都可以。

举个例子,在 5.3.2 的代码基础上加一个判断矩形是否能容纳下另一个矩形的功能(不考虑斜着放,也不考虑矩形的长比宽长的情况):

impl Rectangle { fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.length > other.length } }

逻辑非常好想:只要矩形的宽和长都比另一个大就行。

然后再在main函数里声明几个Rectangle实例,输出比较结果看看有没有问题就行。以下是完整代码:

struct Rectangle { width: u32, length: u32, } impl Rectangle { fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.length > other.length } } fn main() { let rect1 = Rectangle{ width: 30, length: 50, }; let rect2 = Rectangle{ width: 10, length: 40, }; println!("{}", rect1.can_hold(&rect2)); }

输出:

true

5.3.6. 关联函数

可以在impl块里定义不把self作为第一个参数的函数,叫关联函数(不是方法)。它不是在实例上调用的,但它与这个类型有关联。例如:String::from()就是String这个类型上叫做from的关联函数。

关联函数通常用于构造器,也就是用来创建关联类型的一个实例。

比如说,在 5.3.2 的代码基础上加一个构建正方形的构造器(正方形也是特殊的矩形):

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

参数只需要一个,因为构造正方形只需要一个边长。

main函数里调用一下这个关联函数试试,其格式为类型名::函数名(参数)。以下是完整代码:

#[derive(Debug)] struct Rectangle { width: u32, length: u32, } impl Rectangle { fn square(size: u32) -> Rectangle { Rectangle{ width: size, length: size, } } } fn main() { let square = Rectangle::square(10); println!("{:?}", square); }

输出:

Rectangle { width: 10, length: 10 }

::不仅可以用于关联函数,也可以用于模块创建命名空间(以后会讲)。

5.3.7. 多个impl

每个struct允许拥有多个impl块。

比如我要把这篇文章里写过的所有方法和关联函数都写到一个代码示例里。
可以这么写(多个impl块):

#[derive(Debug)] struct Rectangle { width: u32, length: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.length } } impl Rectangle { fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.length > other.length } } impl Rectangle { fn square(size: u32) -> Rectangle { Rectangle{ width: size, length: size, } } } fn main() { let square = Rectangle::square(10); println!("{:?}", square); }

也可以这么写,合在一个impl块里:

#[derive(Debug)] struct Rectangle { width: u32, length: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.length } fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.length > other.length } fn square(size: u32) -> Rectangle { Rectangle{ width: size, length: size, } } } fn main() { let square = Rectangle::square(10); println!("{:?}", square); }
http://www.jsqmd.com/news/1249435/

相关文章:

  • Django毕业设计-基于 Django 的物资仓储与配送管理系统设计与实现 智能物资配送调度管理平台的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 2026 年 7 月新发布:内蒙古可靠的古建牌楼工程制造商哪家专业,揭秘:牌楼的千年工艺,如何重塑现代都市面貌? - 行业甄选官
  • 深入解析C2000 eQEP模块:正交编码器硬件解码与高精度运动控制
  • WSL2配置tensorflow GPU环境
  • 为什么有的人偏爱 Mac?
  • GEO优化为什么不能只发软文?广拓时代谈AI搜索内容资产建设
  • 南京高端首饰回收哪家不坑?2026 新手出手品牌首饰防套路安全变现指南 - 全国二奢机构参考
  • 如何在OpenWrt软路由中增加一个新功能
  • 【办公类-54-03】20240828班级点名册模版(双休国定假涂成灰色)2024学年第一学期
  • 3D格式转换之CREO 转 CATIA 标准化转换技术
  • 2026年成都实力强香港留学机构推荐:五家优选深度解析 - 科技焦点
  • 想了解空气预热器等锅炉部件公司?这些不容错过!
  • 滞回运放电路仿真和分析
  • MCP协议:AI工具互联互通的标准解决方案
  • C++:AVL树
  • 工业总线多源异构协议免编程统一转换:基于流编排的边缘清洗架构与合并实战
  • 粉剂包装机十大品牌,广州恒尔品质靠谱获一致好评 - 品牌速递
  • 语言模型语义不确定性校准:从概率原理到工程实践
  • 民宿 厂区核心景观设计施工选哪家?杭州美村美户商用景观一站式打造全维度详解 - 品牌测评网
  • VASP用于进行自洽场(SCF)计算,INCAR文件配置
  • 基于YOLO26的野生动物智能监测系统设计与优化
  • TM4C1292看门狗定时器原理与实战:从寄存器配置到高可靠设计
  • PLL-分频器
  • Kimi K3重磅发布!2.8万亿参数,碾压开源阵营?
  • 工业5G边缘计算节点IEC 62443合规实践:基于内核 Netfilter 的深度状态防火墙与工业路由器安全隔离完整架构解析
  • Django毕业设计-基于 Django 的鲜花线上销售与配送管理系统 个性化鲜花定制电商平台的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • SIM820X-M2 5G HAT OpenWrt软路由器——安装MySQL(三)问题记录
  • 劳力士苏州售后服务网点地址及客户服务热线2026年7月最新 - 劳力士服务中心
  • “十五五”规划锚定体育强国目标 惊奇体育智慧校园方案助力青少年体育数字化发展 - 米諾
  • 茂名2026年中央空调回收实测:五星推荐本土老牌商家 - 广东再生资源回收