Rust_Chapter_2_通用编程概念

Rust_Chapter_2_通用编程概念

你可能会好奇什么是通用编程概念,其实在我看的这一本经典书中这一章节内容主要是Rust的变量、函数、注释、if表达式及循环。而变量由于其特性十分有趣,我就在上一章节单独拿了出来为一章了。

函数

在Rust中,函数定义以fn关键字开始并紧随函数名称与一对圆括号,另外还有一对花括号用于标识函数体开始和结尾的地方。

1
2
3
4
5
6
fn main(){
sayhello();//可以用这样的方式来调用函数,这很容易
}
fn sayhello(){
println!("Hello");
}

Rust不关心你在何处定义函数,函数定义在main()前后都行,只要这些定义对于使用区域是可见的即可

函数参数

你也可以在函数声明中定义参数,它们是一种特殊的变量,并被视作函数签名的一部分。当函数存在参数时,你需要在调用函数时为这些变量提供具体的值。参数变量和传入的具体参数值有自己分别对应的名称parameterargument,但我们通常会混用两者并将它们统一地称为参数而不加以区别。
很简单,和众多语言的例子一样,直接看代码

1
2
3
4
5
6
7
fn 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
7
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}

注意上面的5不是语句而是表达式,Rust中的函数会返回最后一个表达式,但你也可以使用return语句来返回值
注意如果函数最后还是分号结尾的语句而非表达式的话,实际上返回的是(),会和函数声明的类型相悖,由于不允许隐式转化,也无法转化的规则存在,这样会引起编译器错误

注释

  • 普通注释,其内容将被编译器忽略掉:
    // 单行注释,注释内容直到行尾。
    /* 块注释,注释内容一直到结束分隔符。 */
  • 文档注释,其内容将被解析成 HTML 帮助文档:
    /// 为接下来的项生成帮助文档。
    //! 为注释所属于的项(译注:如 crate、模块或函数)生成帮助文档。

if表达式

1
2
3
4
5
6
7
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}

与C/C++类似,但有几个不同或需要注意的地方

  • 分支条件无需写括号,但不是说不能写
  • if不支持C/C++那样的单行写法,大括号必须要有
  • Rust期望在条件表达式中获得一个bool值,而不是一个整数。Rust不会自动尝试将非布尔类型的值转换为布尔类型。你必须显式地在if表式中提供一个布尔类型作为条件
  • Rust会且仅会执行第一个条件为真的代码块,一旦发现满足的条件,它便不会再继续检查剩余的那些条件分支了。

注意我们这一节说if表达式,而不是语句是有原因的,比如在let中是可以使用表达式的

1
2
3
4
5
6
7
8
9
10
fn 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
9
fn 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种循环:loopwhilefor

loop

我们可以使用loop关键字来指示Rust反复执行某一块代码,直到我们显式地声明退出为止。

1
2
3
4
5
fn main() {
loop {
println!("again!");
}
}

loop相当于while(true),需要break来弹出,否则就会无限循环
在break后面跟着返回的值,就可以设置loop表达式返回的值
注意所有循环中都有break,但只有loopbreak才能跟返回值,如下例子
1
2
3
4
5
6
7
8
9
10
fn 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
8
fn 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
10
fn 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
6
fn 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编译器在解析代码的时候,如果碰到分号,就会继续往后面执行;如果碰到语句,则执行语句;如果碰到表达式,则会对表达式求值,如果分号后面什么都没有,就会补上单元值()