第9篇:成员功能——为结构体添加能力 Rust中文编程

第9篇:成员功能------为结构体添加能力

作者: 李金雨
联系方式: wbtm2718@qq.com
目标读者: Rust中文编程
核心理念: AI时代必须使用中文编程,母语编程阅读效率极高


1. 开篇引入

本课目标

  • 理解什么是成员功能(方法)
  • 掌握为结构体实现方法的方法
  • 理解方法中的self参数
  • 理解关联函数的概念
  • 理解方法与函数的区别

生活场景引入

想象一下,你有一个机器人,它有各种功能:

  • 走路
  • 说话
  • 唱歌
  • 跳舞

这些功能是机器人的一部分,只能由机器人来执行。在编程中,方法就像机器人的功能,它是结构体的一部分,只能由结构体的实例来调用。

预期成果展示

通过这节课的学习,你将能够:

  • 为结构体实现方法
  • 调用结构体的方法
  • 理解方法中的self参数
  • 定义和使用关联函数
  • 区分方法与函数

2. 概念讲解

什么是成员功能(方法)

方法是与结构体关联的函数,它可以访问和修改结构体的字段。方法就像结构体的行为,是结构体的一部分。

为结构体实现方法

在Rust中,我们使用impl块来为结构体实现方法:

rust 复制代码
fn main() {
    impl 结构体名 {
        fn 方法名(&self, 参数: 类型) -> 返回类型 {
            // 方法体
        }
    }
}

例如,为学生结构体实现方法:

rust 复制代码
fn main() {
    struct 学生 {
        姓名: String,
        年龄: i32,
        成绩: i32,
        科目列表: Vec<String>,
    }

    impl 学生 {
        // 显示信息方法
        fn 显示信息(&self) {
            println!("姓名:{}", self.姓名);
            println!("年龄:{}岁", self.年龄);
            println!("成绩:{}分", self.成绩);
            println!("学习的科目:{:?}", self.科目列表);
        }

        // 学习方法
        fn 学习(&mut self, 科目: &str) {
            self.科目列表.push(String::from(科目));
            println!("{}正在学习{}", self.姓名, 科目);
        }

        // 考试方法
        fn 考试(&mut self, 分数: i32) {
            self.成绩 = 分数;
            println!("{}考了{}分", self.姓名, 分数);
        }
    }
}

self参数

方法的第一个参数通常是self,它代表调用方法的实例。self有三种形式:

  1. &self:不可变引用,用于只读操作
  2. &mut self:可变引用,用于修改操作
  3. self:值传递,会转移所有权
rust 复制代码
fn main() {
    struct 学生 {
        姓名: String,
        年龄: i32,
        成绩: i32,
        科目列表: Vec<String>,
    }

    impl 学生 {
        // 不可变方法(只读)
        fn 显示信息(&self) {
            // 只能读取字段,不能修改
        }

        // 可变方法(可修改)
        fn 学习(&mut self, 科目: &str) {
            // 可以修改字段
        }

        // 消费方法(转移所有权)
        fn 转换为字符串(self) -> String {
            format!("{},{}岁,{}分", self.姓名, self.年龄, self.成绩)
        }
    }
}

方法的调用

调用方法使用点号(.):

rust 复制代码
fn main() {
    struct 学生 {
        姓名: String,
        年龄: i32,
        成绩: i32,
        科目列表: Vec<String>,
    }

    impl 学生 {
        fn 显示信息(&self) {
            println!("姓名:{},年龄:{}岁,成绩:{}分", self.姓名, self.年龄, self.成绩);
        }

        fn 学习(&mut self, 科目: &str) {
            self.科目列表.push(String::from(科目));
        }

        fn 考试(&mut self, 分数: i32) {
            self.成绩 = 分数;
        }
    }

    let mut 学生一 = 学生 {
        姓名: String::from("张三"),
        年龄: 15,
        成绩: 0,
        科目列表: vec![],
    };

    // 调用显示信息方法
    学生一.显示信息();

    // 调用学习方法
    学生一.学习("数学");

    // 调用考试方法
    学生一.考试(95);

    // 再次显示信息
    学生一.显示信息();
}

关联函数

关联函数是与结构体关联的函数,但它不接收self参数。关联函数通常用于创建结构体实例,就像构造函数一样。

rust 复制代码
fn main() {
    struct 学生 {
        姓名: String,
        年龄: i32,
        成绩: i32,
        科目列表: Vec<String>,
    }

    impl 学生 {
        // 关联函数(构造函数)
        fn 新(姓名: String, 年龄: i32) -> Self {
            Self {
                姓名,
                年龄,
                成绩: 0,
                科目列表: vec![],
            }
        }
    }
}

调用关联函数使用::操作符:

rust 复制代码
fn main() {
    struct 学生 {
        姓名: String,
        年龄: i32,
        成绩: i32,
        科目列表: Vec<String>,
    }

    impl 学生 {
        fn 新(姓名: String, 年龄: i32) -> Self {
            Self {
                姓名,
                年龄,
                成绩: 0,
                科目列表: vec![],
            }
        }
    }

    let 学生一 = 学生::新(String::from("张三"), 15);
}

方法与函数的区别

区别 函数 方法
定义位置 任何地方 impl块内
调用方式 直接调用 通过实例调用
self参数 有(通常)
访问权限 只能通过参数访问数据 可以直接访问实例的字段

3. 动手实践

基础练习(必做)

  1. 为"动物"结构体添加"叫"的方法

    rust 复制代码
    struct 动物 {
        名称: String,
        年龄: i32,
        声音: String,
    }
    
    impl 动物 {
        // 关联函数(构造函数)
        fn 新(名称: String, 年龄: i32, 声音: String) -> Self {
            Self {
                名称,
                年龄,
                声音,
            }
        }
        
        // 叫的方法
        fn 叫(&self) {
            println!("{}在{}", self.名称, self.声音);
        }
        
        // 显示信息方法
        fn 显示信息(&self) {
            println!("名称:{}", self.名称);
            println!("年龄:{}岁", self.年龄);
            println!("声音:{}", self.声音);
        }
    }
    
    fn main() {
        let 猫 = 动物::新(String::from("猫咪"), 2, String::from("喵喵"));
        let 狗 = 动物::新(String::from("狗狗"), 3, String::from("汪汪"));
        
        猫.显示信息();
        猫.叫();
        
        狗.显示信息();
        狗.叫();
    }
  2. 为"汽车"结构体添加"加速"、"减速"方法

    rust 复制代码
    struct 汽车 {
        品牌: String,
        颜色: String,
        速度: f64,
    }
    
    impl 汽车 {
        // 关联函数(构造函数)
        fn 新(品牌: String, 颜色: String) -> Self {
            Self {
                品牌,
                颜色,
                速度: 0.0,
            }
        }
        
        // 加速方法
        fn 加速(&mut self, 增量: f64) {
            self.速度 += 增量;
            println!("{}加速了{}km/h,当前速度:{}km/h", self.品牌, 增量, self.速度);
        }
        
        // 减速方法
        fn 减速(&mut self, 减量: f64) {
            if self.速度 >= 减量 {
                self.速度 -= 减量;
                println!("{}减速了{}km/h,当前速度:{}km/h", self.品牌, 减量, self.速度);
            } else {
                self.速度 = 0.0;
                println!("{}减速到0km/h", self.品牌);
            }
        }
        
        // 显示信息方法
        fn 显示信息(&self) {
            println!("品牌:{}", self.品牌);
            println!("颜色:{}", self.颜色);
            println!("当前速度:{}km/h", self.速度);
        }
    }
    
    fn main() {
        let mut 我的汽车 = 汽车::新(String::from("大众"), String::from("黑色"));
        我的汽车.显示信息();
        
        我的汽车.加速(30.0);
        我的汽车.加速(20.0);
        我的汽车.减速(10.0);
        我的汽车.减速(50.0);
    }
  3. 为"书籍"结构体添加"显示信息"方法

    rust 复制代码
    struct 书籍 {
        书名: String,
        作者: String,
        价格: f64,
        页数: i32,
    }
    
    impl 书籍 {
        // 关联函数(构造函数)
        fn 新(书名: String, 作者: String, 价格: f64, 页数: i32) -> Self {
            Self {
                书名,
                作者,
                价格,
                页数,
            }
        }
        
        // 显示信息方法
        fn 显示信息(&self) {
            println!("书名:{}", self.书名);
            println!("作者:{}", self.作者);
            println!("价格:{}元", self.价格);
            println!("页数:{}页", self.页数);
        }
        
        // 计算每页数价格的方法
        fn 每页数价格(&self) -> f64 {
            self.价格 / self.页数 as f64
        }
    }
    
    fn main() {
        let 书籍一 = 书籍::新(
            String::from("Rust编程入门"),
            String::from("李金雨"),
            49.9,
            300
        );
        
        书籍一.显示信息();
        println!("每页数价格:{:.4}元", 书籍一.每页数价格());
    }

进阶练习(选做)

  1. 为"点"结构体添加方法

    rust 复制代码
    struct 点 {
        x: f64,
        y: f64,
    }
    
    impl 点 {
        // 关联函数(构造函数)
        fn 新(x: f64, y: f64) -> Self {
            Self { x, y }
        }
        
        // 计算到原点的距离
        fn 到原点距离(&self) -> f64 {
            (self.x * self.x + self.y * self.y).sqrt()
        }
        
        // 计算到另一个点的距离
        fn 到点距离(&self, 另一个点: &点) -> f64 {
            let 差x = 另一个点.x - self.x;
            let 差y = 另一个点.y - self.y;
            (差x * 差x + 差y * 差y).sqrt()
        }
        
        // 移动点
        fn 移动(&mut self, dx: f64, dy: f64) {
            self.x += dx;
            self.y += dy;
        }
    }
    
    fn main() {
        let mut 点一 = 点::新(0.0, 0.0);
        let 点二 = 点::新(3.0, 4.0);
        
        println!("点一:({}, {})", 点一.x, 点一.y);
        println!("点二:({}, {})", 点二.x, 点二.y);
        println!("点一到原点的距离:{}", 点一.到原点距离());
        println!("点一到点二的距离:{}", 点一.到点距离(&点二));
        
        点一.移动(1.0, 2.0);
        println!("移动后点一:({}, {})", 点一.x, 点一.y);
        println!("移动后点一到点二的距离:{}", 点一.到点距离(&点二));
    }
  2. 为"矩形"结构体添加方法

    rust 复制代码
    struct 矩形 {
        长: f64,
        宽: f64,
    }
    
    impl 矩形 {
        // 关联函数(构造函数)
        fn 新(长: f64, 宽: f64) -> Self {
            Self { 长, 宽 }
        }
        
        // 计算面积
        fn 面积(&self) -> f64 {
            self.长 * self.宽
        }
        
        // 计算周长
        fn 周长(&self) -> f64 {
            2.0 * (self.长 + self.宽)
        }
        
        // 判断是否为正方形
        fn 是正方形(&self) -> bool {
            self.长 == self.宽
        }
    }
    
    fn main() {
        let 矩形一 = 矩形::新(5.0, 3.0);
        let 矩形二 = 矩形::新(4.0, 4.0);
        
        println!("矩形一:长{},宽{}", 矩形一.长, 矩形一.宽);
        println!("面积:{}", 矩形一.面积());
        println!("周长:{}", 矩形一.周长());
        println!("是否为正方形:{}", 矩形一.是正方形());
        
        println!("\n矩形二:长{},宽{}", 矩形二.长, 矩形二.宽);
        println!("面积:{}", 矩形二.面积());
        println!("周长:{}", 矩形二.周长());
        println!("是否为正方形:{}", 矩形二.是正方形());
    }

挑战练习(拓展)

  1. 为"学生管理系统"添加方法

    rust 复制代码
    struct 学生 {
        姓名: String,
        年龄: i32,
        成绩: i32,
    }
    
    impl 学生 {
        // 关联函数(构造函数)
        fn 新(姓名: String, 年龄: i32, 成绩: i32) -> Self {
            Self {
                姓名,
                年龄,
                成绩,
            }
        }
        
        // 显示信息方法
        fn 显示信息(&self) {
            println!("姓名:{},年龄:{}岁,成绩:{}分", 
                     self.姓名, self.年龄, self.成绩);
        }
        
        // 修改成绩方法
        fn 修改成绩(&mut self, 新成绩: i32) {
            self.成绩 = 新成绩;
            println!("{}的成绩已修改为{}分", self.姓名, self.成绩);
        }
    }
    
    struct 学生管理系统 {
        学生们: Vec<学生>,
    }
    
    impl 学生管理系统 {
        // 关联函数(构造函数)
        fn 新() -> Self {
            Self {
                学生们: vec![],
            }
        }
        
        // 添加学生方法
        fn 添加学生(&mut self, 学生: 学生) {
            self.学生们.push(学生);
            println!("学生添加成功");
        }
        
        // 显示所有学生信息方法
        fn 显示所有学生(&self) {
            println!("学生列表:");
            for (索引, 学生) in self.学生们.iter().enumerate() {
                print!("第{}名:", 索引 + 1);
                学生.显示信息();
            }
        }
        
        // 计算平均成绩方法
        fn 计算平均成绩(&self) -> f64 {
            if self.学生们.is_empty() {
                return 0.0;
            }
            
            let mut 总分 = 0;
            for 学生 in &self.学生们 {
                总分 += 学生.成绩;
            }
            
            总分 as f64 / self.学生们.len() as f64
        }
    }
    
    fn main() {
        let mut 系统 = 学生管理系统::新();
        
        // 添加学生
        系统.添加学生(学生::新(String::from("张三"), 15, 95));
        系统.添加学生(学生::新(String::from("李四"), 14, 88));
        系统.添加学生(学生::新(String::from("王五"), 15, 92));
        
        // 显示所有学生
        系统.显示所有学生();
        
        // 计算平均成绩
        println!("平均成绩:{}分", 系统.计算平均成绩());
        
        // 修改学生成绩
        if let Some(学生) = 系统.学生们.get_mut(0) {
            学生.修改成绩(98);
        }
        
        // 再次显示所有学生
        系统.显示所有学生();
        // 再次计算平均成绩
        println!("平均成绩:{}分", 系统.计算平均成绩());
    }

4. 知识总结

核心概念回顾

  • 方法:与结构体关联的函数,通过实例调用
  • impl块:用于为结构体实现方法
  • self参数:方法的第一个参数,代表调用方法的实例
  • &self:不可变引用,用于只读操作
  • &mut self:可变引用,用于修改操作
  • 关联函数:与结构体关联但不接收self参数的函数
  • 构造函数:通常是关联函数,用于创建结构体实例

关键代码速查

功能 代码 说明
方法定义 fn 方法名(&self, 参数: 类型) -> 返回类型 { 代码 } 定义一个方法
可变方法 fn 方法名(&mut self, 参数: 类型) -> 返回类型 { 代码 } 定义一个可以修改实例的方法
关联函数 fn 函数名(参数: 类型) -> Self { 代码 } 定义一个关联函数
方法调用 实例.方法名(参数) 调用实例的方法
关联函数调用 结构体名::函数名(参数) 调用关联函数

常见错误提醒

  1. self参数:方法的第一个参数通常是self
  2. 引用类型:根据需要选择&self、&mut self或self
  3. 方法调用:方法需要通过实例调用,而不是直接调用
  4. 关联函数调用:关联函数需要通过结构体名调用

5. 课后作业

巩固练习题

  1. 编写一个程序,为"手机"结构体添加"打电话"、"发短信"方法
  2. 编写一个程序,为"学校"结构体添加"添加学生"、"显示学生"方法
  3. 编写一个程序,为"电影"结构体添加"播放"、"暂停"方法

创意编程题

  1. 发挥你的创意,创建一个程序,为"游戏角色"结构体添加"攻击"、"防御"、"移动"方法
  2. 编写一个程序,为"银行账户"结构体添加"存款"、"取款"、"查询余额"方法

下篇预习提示

下一篇我们将学习"归属权与借用------Rust的安全保障",这是Rust的特色功能,能够保证内存安全。预习一下:

  • 什么是归属权(所有权)?
  • 为什么Rust需要归属权?
  • 什么是借用?
  • 借用有什么规则?

恭喜你完成了第九篇教程!你已经掌握了为结构体添加方法的技巧,学会了如何让结构体具有更多的功能。继续加油,下一篇我们将学习Rust的特色功能------所有权和借用!

学习交流

如果你在学习过程中遇到任何问题,欢迎联系我:

  • ** 视频号,"时空系"
  • 邮箱: wbtm2718@qq.com
  • 其他提示:
  • 本人正在开发纯中文编程语言和编译器,有兴趣的可以交流
  • 本人正在写一本基于中国母语思维习惯的数学教材,可以让你一年内学完从初中到研究生的数学,而且可以让你快速掌握人工智能的关键数学知识,有兴趣的可以交流

祝你学习愉快!

相关推荐
@encryption2 小时前
VLAN --- 深度学习
网络
MATLAB代码顾问2 小时前
多种群协同进化算法(MPCE)求解大规模作业车间调度问题——附MATLAB代码
开发语言·算法·matlab
代码小书生2 小时前
statistics,一个统计的 Python 库!
开发语言·python
@insist1232 小时前
信息安全工程师-虚拟专用网络核心技术与软考考点全解析
网络·软考·信息安全工程师·软件水平考试
摇滚侠2 小时前
整洁的桌面和任务栏 Java 开发工程师提效方法
java·开发语言
知识分享小能手2 小时前
R语言入门学习教程,从入门到精通,R语言数据计算与分组统计(9)
开发语言·学习·r语言
山居秋暝LS3 小时前
安装C++版opencv和opencv_contrib
开发语言·c++·opencv
老陈说编程3 小时前
12. LangChain 6大核心调用方法:invoke/stream/batch同步异步全解析,新手也能轻松学会
开发语言·人工智能·python·深度学习·机器学习·ai·langchain
以太浮标3 小时前
华为eNSP模拟器综合实验之- MGRE多点GRE隧道详解
运维·网络·网络协议·网络安全·华为·信息与通信