Rust_Chapter_2_通用编程概念
你可能会好奇什么是通用编程概念,其实在我看的这一本经典书中这一章节内容主要是Rust的变量、函数、注释、if表达式及循环。而变量由于其特性十分有趣,我就在上一章节单独拿了出来为一章了。
函数
在Rust中,函数定义以fn
关键字开始并紧随函数名称与一对圆括号,另外还有一对花括号用于标识函数体开始和结尾的地方。1
2
3
4
5
6fn main(){
sayhello();//可以用这样的方式来调用函数,这很容易
}
fn sayhello(){
println!("Hello");
}
Rust不关心你在何处定义函数,函数定义在main()
前后都行,只要这些定义对于使用区域是可见的即可
函数参数
你也可以在函数声明中定义参数,它们是一种特殊的变量,并被视作函数签名的一部分。当函数存在参数时,你需要在调用函数时为这些变量提供具体的值。参数变量和传入的具体参数值有自己分别对应的名称parameter和argument,但我们通常会混用两者并将它们统一地称为参数而不加以区别。
很简单,和众多语言的例子一样,直接看代码1
2
3
4
5
6
7fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
//输出The value of x is: 5
出于严谨的考虑,在Rust的函数签名中,你必须显式地声明每个参数的类型。
函数的返回值
函数可以向调用它的代码返回值。虽然你不用为这个返回值命名,但需要在箭头符号->
的后面声明它的类型,类型在函数定义时时确定的。1
2
3
4
5
6
7fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
注意上面的5不是语句而是表达式,Rust中的函数会返回最后一个表达式,但你也可以使用return
语句来返回值
注意如果函数最后还是分号结尾的语句而非表达式的话,实际上返回的是()
,会和函数声明的类型相悖,由于不允许隐式转化,也无法转化的规则存在,这样会引起编译器错误
注释
- 普通注释,其内容将被编译器忽略掉:
//
单行注释,注释内容直到行尾。
/*
块注释,注释内容一直到结束分隔符。*/
- 文档注释,其内容将被解析成 HTML 帮助文档:
///
为接下来的项生成帮助文档。
//!
为注释所属于的项(译注:如 crate、模块或函数)生成帮助文档。
if表达式
1 | if n < 0 { |
与C/C++类似,但有几个不同或需要注意的地方
- 分支条件无需写括号,但不是说不能写
- if不支持C/C++那样的单行写法,大括号必须要有
- Rust期望在条件表达式中获得一个bool值,而不是一个整数。Rust不会自动尝试将非布尔类型的值转换为布尔类型。你必须显式地在if表式中提供一个布尔类型作为条件
- Rust会且仅会执行第一个条件为真的代码块,一旦发现满足的条件,它便不会再继续检查剩余的那些条件分支了。
注意我们这一节说if表达式
,而不是语句是有原因的,比如在let
中是可以使用表达式的1
2
3
4
5
6
7
8
9
10fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
}
//输出结果 The value of number is: 5
这也意味着,所有if分支可能返回的值都必须是一种类型的;if分支与else分支的结果都是i32类型的整数。而如下运行这段代码会导致编译时错误,因为if与else分支产生了不同类型的值。1
2
3
4
5
6
7
8
9fn main() {
let condition = true;
let number = if condition {
5
} else {
"six"//ERROR!!!
};
println!("The value of number is: {}", number);
}
为了对其他使用number变量的代码进行编译时类型检查,Rust需要在编译时确定number的具体类型。如果Rust能够使用运行时确定的number类型,那么它就不得不记录变量所有可能出现的类型,这会使得编译器的实现更加复杂,并丧失许多代码安全保障
循环表达式
Rust提供了3种循环:loop
、while
和for
。
loop
我们可以使用loop关键字来指示Rust反复执行某一块代码,直到我们显式地声明退出为止。1
2
3
4
5fn main() {
loop {
println!("again!");
}
}loop
相当于while(true)
,需要break
来弹出,否则就会无限循环
在break后面跟着返回的值,就可以设置loop表达式返回的值了
注意所有循环中都有break
,但只有loop
的break
才能跟返回值,如下例子1
2
3
4
5
6
7
8
9
10fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
while
while
会在条件为真时重复执行代码,while
也不需要一定需要加括号1
2
3
4
5
6
7
8fn main() {
let mut n = 1;
while n < 101 {
println!("{}",n);
n += 1;
}
}
//输出1到100
for
用for循环来遍历集合中的每一个元素。for in
结构可以遍历一个Iterator
(迭代器)。创建迭代器的一个最简单的方法是使用区间标记a..b
。这会生成从a
(包含此值)到b
(不含此值)的,步长为1的一系列值,如果使用a..=b
表示两端都包含在内的范围1
2
3
4
5
6
7
8
9
10fn main() {
for number in 1..=4 {
println!("{}!", number);
}//输出1! 2! 3! 4!
for number in (1..4).rev() {
//rev()是迭代器的方法,可以翻转序列
println!("{}!", number);
}//输出3! 2! 1!
}
而迭代器显然的也可以用于一个数组,太典了1
2
3
4
5
6fn main() {
let a = [1,2,3,4,5];
for number in a.iter() {
println!("{}!", number);
}//输出1! 2! 3! 4! 5!
}
语句和表达式语义的区分
由于Rust是一门基于表达式的语言,所以它将语句/statement与表达式/expression区别为两个不同的概念,这与其他某些语言不同,因此区分他俩就显得十分重要了
- 语句:指的是要执行的一些操作和产生副作用的表达式。
- 表达式:主要用于计算求值。
而语句又可以分为两类,声明语句与表达式语句
- 声明语句:用于声明各种语言项,包括变量、静态变量、常量、结构体、函数等,以及通过extern和use关键字引入包和模块
- 表达式语句:特指以分号结尾的表达式。此类表达式求值结果将会被舍弃,并总是返回单元类型()
当遇到函数的时候,会将函数体的花括号识别为块表达式。块表达式是由一对花括号和一系列表达式组成的,它总是返回块中最后一个表达式的值。
Rust编译器在解析代码的时候,如果碰到分号,就会继续往后面执行;如果碰到语句,则执行语句;如果碰到表达式,则会对表达式求值,如果分号后面什么都没有,就会补上单元值()