前言
上一篇,介绍了Rust语言的
-
结构体
-
枚举
如果类型(Types)只有属性,没有动态行为,显然是不够的,在实际场景中,我们需要与类型有些互动,比如利用函数(function)或方法(method)得到返回值,或者可以对已有属性进行操作或修改。
Rust为我们准备了 impl blocks,字面看来,就是为类型实现(implementations)点什么,其同时适用于用户自定义和任何封装类型。
那么,这一篇,我们介绍的函数和方法实现涉及
-
结构体的函数和方法实现
-
枚举的函数和方法实现
-
模块简介
结构体之功能实现
这里的代码为之前写过的Player 结构添加两个功能:
-
第9行:记住impl这个关键字
-
第10-16行:一个类似于构造函数的函数(dong xi),接受名称并为Person中的其余字段设置默认值
-
第18-24行:两个方法函数,get_friends和set_friends,分别负责取名和取数
// struct_methods.rs
struct Player {
name: String,
iq: u8,
friends: u8
}
impl Player {
fn with_name(name: &str) -> Player {
Player {
name: name.to_string(),
iq: 100,
friends: 100
}
}
fn get_friends(&self) -> u8 {
self.friends
}
fn set_friends(&mut self, count: u8) {
self.friends = count;
}
}
fn main() {
let mut player = Player::with_name("Dave");
player.set_friends(23);
println!("{}'s friends count: {}", player.name, player.get_friends());
// another way to call instance methods.
let _ = Player::get_friends(&player);
}
代码结果为:
在写实现的时候,通常会有两种方法(methods)可写:
-
Associated methods:这种方法,不需要self 类型作为第一参数,上面示例中的with_name 方法就是这种,类似于面向对象编程中的静态方法(static method),可以认为这种方法是自身具足的,参见第28行
-
Instance methods:该方法以self为第一参数,与Python类似,因此只能借由实例(instance)唤起,参见第29行,而第32行提供了另一种调用该类方法的方式(读者可以自己试一下,如果不用指针类型作为参数,会出现什么情况?)
再看一下 set_friends方法是参数 &mut self,表明self是在运行时从类型实例中借用过来的,如果不用&,则会把实例的值赋给该方法,等运行之后,又会重新释放。
读到这里,好像有点不知所云,请读者记住这一点,很快就会在内存管理与安全的部分给大家一个交代。
枚举之功能实现
这里进入枚举部分,看一个有关支付的代码示例,
// enum_methods.rs
enum PaymentMode {
Debit,
Credit,
Paypal
}
// Bunch of dummy payment handlers
fn pay_by_credit(amt: u64) {
println!("Processing credit payment of {}", amt);
}
fn pay_by_debit(amt: u64) {
println!("Processing debit payment of {}", amt);
}
fn paypal_redirect(amt: u64) {
println!("Redirecting to paypal for amount: {}", amt);
}
impl PaymentMode {
fn pay(&self, amount: u64) {
match self {
PaymentMode::Debit => pay_by_debit(amount),
PaymentMode::Credit => pay_by_credit(amount),
PaymentMode::Paypal => paypal_redirect(amount)
}
}
}
fn get_saved_payment_mode() -> PaymentMode {
PaymentMode::Debit
}
fn main() {
let payment_mode = get_saved_payment_mode();
payment_mode.pay(512);
}
代码结果如下
以上代码非常明朗,这里的实现方法名称为:get_saved_payment_mode()用来返回用户的付款模式:Credit Card, Debit Card, or Paypal显然很适合用枚举建模,这3种模式显然作为枚举变体。
在pay方法中,用户可以任选三种付款方式之一进行支付,该方法对应决定使用哪一种,而后进行指派。请读者最后自己按照具体场景,敲一下这个代码,相信会感到这里面枚举所起到的作用是非常有恰当的。
进一步说来,枚举广泛用于状态机建模,当与match语句结合使用时,可以使状态转换代码编写起来非常简洁;还可以用于自定义错误类型方面的建模。
当enum变量没有与之关联的任何数据时,可以像C枚举那样使用,其中的变量隐式的具有从0开始的整数值,但也可以手动标记为整数值(isize),而这在与外部C库交互时还是很有用的。
模块(modules)简介
任何编程语言都会提供一种方法,将相对繁重的代码拆分为多个文件,以管理复杂性,Rust使用模块来做这件事,有关模块的详细介绍,会放在本系列的第二部分。这里列出一些重点,作为本部分后续章节的基础。
-
每个Rust程序都需要有一个root模块。在可执行文件中,通常是main.rs,对于libraries,是libraries .rs
-
模块可以在其他模块中声明,也可以作为文件和目录
-
root模块要使用mod关键字来声明,以保证编译器可以识别
-
要使用模块内的任何项目,需要使用use关键字以及模块的名称来引用
-
模块中定义的项目默认情况下是私有的,需要使用pub关键字将其公开
结语
可以看出,本篇与上一篇形成一一对应,上一篇重点在于如何定义结构体和枚举的属性,本篇介绍了如何与这两种用户自定义类型进行互动,并对之进行操作,而使这些代码编写变得灵活,表现力充分增强,效果自然可期。难怪某位Rust中文社区的一位资深成员谈及:这么多年了,Rust让我又感到了编程的乐趣。
下一篇,我们看下集合体与迭代器。
主要参考和建议读者进一步阅读的文献
https://doc.rust-lang.org/book
1.Rust编程之道,2019, 张汉东
2.The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger
3.Hands-On Data Structures and Algorithms with Rust,2018,Claus Matzinger
4.Beginning Rust ,2018,Carlo Milanesi
5.Rust Cookbook,2017,Vigneshwer Dhinakaran