Rust之抽空学习系列(四)—— 编程通用概念(下)
Rust之抽空学习系列(四)—— 编程通用概念(下)
1、函数
函数用来对功能逻辑进行封装,能够增强复用、提高代码的可读
以下是函数的主要组成部分:
- 名称
- 参数
- 返回类型
- 函数体
1.1、函数名称
在Rust中,函数通过fn
关键字进行声明,并且函数的名称遵循蛇形命名法,即名称使用小写字母进行组合,单词与单词之间使用下划线进行分割,这点倒是和Python中的函数命名方式倒是相一致的
其实,在Rust中函数和变量通常都应该采用蛇形命名法
维基百科:蛇形命名法
fn make_money() { // 蛇形命名法
println!("赚钱");
}
fn main() { // 作为入口的main()函数
// 函数调用,函数名加(参数),现在没有参数就是()
make_money();
}
刚刚是先定义的函数,再在main
中进行调用,这个顺序反过来也不影响,这个可以和某些编程语言的写法区分一下
fn main() {
make_money();
}
// 放在后面也可以
fn make_money() {
println!("赚钱");
}
函数可以先定义或后定义,只要对于使用区域可见即可
1.2、函数参数
函数的参数可以将外部的变化灵活地告诉内部的逻辑,在框架不变的前提下随机应变,作为函数签名的一部分,就像在文档上看到的那样
fn main() {
make_money(3); // 传入具体的参数
}
fn make_money(times: i32) { // 定义可传入的参数
println!("赚钱{}次", times);
}
在函数签名当中,需要显式声明每个参数的类型(Rust的设计者慎重考虑的结果),以便于编译器根据其他部分的代码进行推导后能明确意图,显式地标准总好过复杂的外部情形,对吧?
而对于需要传入多个函数参数的场景,参数彼此直接使用,
间隔开即可
fn main() {
make_money(3, 100);
}
// 多个参数,指出类型,“,”分隔
fn make_money(times: i32, value: i32) {
println!("赚钱{}次, 每次{}", times, value);
}
函数中的语句和表达式
先来了解下两个概念:语句和表达式
- 语句是执行操作但是不返回值的指令
- 表达式是会进行计算并且得到一个值作为结果的指令
维基百科:语句&语句与表达式的区别
这样看来,表达式最终还是可以换成某个变量表示的,就像数学计算里一样,我们列了一长串计算的式子,最终的目的也只是为了一个计算结果
图中绿框的部分是语句,因为它们没有返回值,只是在描述一步步地操作,但是第二条语句的右侧红框部分是一条表达式,描述了sentence变量与234比较的结果,最终返回了bool类型
let sentence = 125;
let result = {
let temp = sentence / 2 ;
temp == 234 // 没有分号,用作表达式返回
};
还有些复杂些的场景(比如套{}
的多行语句),在末尾使用表达式,不带分号可以返回结果,因为表达式不包括分号的部分
1.3、函数返回值
函数的返回值也是签名的一部分,可以向调用者返回值,使用->
表示,并在其后面声明其类型
可以使用return
直接返回值或者函数体中最后一个表达式的值进行返回
fn sum(a: i32, b: i32) -> i32 { // 定义类型
a + b // 表达式返回
}
let result = sum(3, 2); // 返回5
println!("result={}", result);
如果为函数的表达式结果加上分号,那么就会变成语句,进而无法匹配返回值类型
可以从报错信息中看出,此时返回值类型不匹配,现在改成语句后编译器接收到的是()
,似乎是空元组,那么其实也可以推测出语句默认返回的值就是空元组了,这正好对应了没有返回值的函数,其实它们的返回值()
只是没有体现在代码中
如果体现出来,可以写成这样,没有返回值的函数其实也是隐含了一个类型的
2、控制流
程序往往不是平铺直叙的,需要包含循环、判断等控制逻辑使其更加丰满
2.1、if 表达式
if
表达式主要是根据条件选择分支,许多的编程语言中都有类似的表达
let assets = 100;
if assets < 100 { // 条件分支
println!("穷人")
} else if assets >= 100 && assets < 10_000 {
println!("普通工薪阶级")
} else { // 兜底
println!("大佬")
}
if
会计算对应分支的条件表达式的bool值,为true则执行对应的代码块的内容,为false则将跳过整个代码块(Rust不会尝试将非布尔类型的值转换为布尔类型)
并且,else if
和else
总是伴随if
,一同组成多条件的分支
接下来,还是需要继续强调一下if
表达式作为表达式的属性,由于if
其实是表达式,那么本身能够返回值,因此可以直接将if
的整体判断和返回内容一同放置到值的位置上
let assets = 100;
let identity = if assets < 100 { // 表达式作为右值
"穷人" // 返回值
} else if assets >= 100 && assets < 10_000 {
"普通工薪阶级"
} else {
"大佬"
};
println!("身份={}", identity);
这里对于原来的内容进行了改写,将if
判断结构直接作为赋值语句的右值使用
需要注意的一点是,在使用if
对于条件进行判断的时候,要确保所有分支返回的类型要统一
Rust编译器需要在编译的时候确认分支返回的类型,需要是某个确定的类型
上图这种就是一个返回了字符串字面量,而另一个分支是一个整数,显然不好确定嘛
2.2、循环
计算机很擅长做重复的工作,并且这样的工作也非常适合它们
在各种编程语言中,提供的循环的结构也都大同小异,Rust也是基于这些原型进行一些优化和改造
Rust提供了3种循环:
- loop
- while
- for
2.2.1、loop循环
使用loop{}
可以定义一段无限循环
loop {
print!("放我出去!")
}
但是,通常我们不会进行无意义的无限循环,还是需要满足一定的条件的时候让它处理一些事情,这个时候就需要使用break
跳出
let mut i = 1;
let result = loop {
i += 1;
if i % 3 == 1 {
break i; // 满足条件结束并返回 4
}
};
println!("result={}", result);
2.2.2、while循环
while循环会在每次执行循环体之前判断一次条件,条件为true就执行,否则就跳出
let mut num = 10;
while num > 0 {
println!("倒数={}", num);
num -= 1;
}
2.2.3、for循环
使用for
最大的好处是可以方便地遍历数组、元组等容器里的元素
let teams = ["姆巴佩", "哈兰德", "克瓦拉茨赫利亚", "贝林厄姆", "穆德里克"];
println!("开始点名!");
for player in teams.iter() {
println!("{}", player);
}
在遍历元素方面,for
显得更加安全简洁
for i in 0 .. teams.len() { // 也可以使用Range通过区间取出索引
println!("{}", i);
}
https://kaisery.github.io/trpl-zh-cn/ch03-05-control-flow.html