Rust基本类型和逻辑规则

编程语言中最基本的就是基本数据类型(premitive type),程序员所有的自定义类型最终都将落实到基础数据类型的排列和操作上。程序的运行需要进行逻辑的控制,在图灵完备的语言中,都具有完善的程序流控制语法。

在Rust中,基本类型、语句/表达式,程序流程控制都被安排得明明白白,这篇博文也将从这3个角度出发。讲解一下Rust中的基本类型和逻辑规则

Rust基础数据类型

在Rust中的基础数据类型有两种: scalarcompound

Scalar Type

scalar的类型有:

  • 有符号整数:i8, i16, i32, i64, i128 and isize(pointer size)
  • 无符号整数:u8, u16, u32, u64, u128 and usize(pointer size)
  • 浮点数: f32, f64
  • char: Unicode scalar value like ​'a', 'α' and '∞'(每个字符4个字节)
  • bool: true or flase
  • unit type: ()​,他是一个空的tuple,虽然unit type是一个tuple,但是它被认为是一个scalar因为它不包含其他类型。

这个地方我们需要留心unit type, 后文中关于表达式的内容会涉及到。

Compound Type

这里介绍Rust中的两种Compound Type: Tuple​ & ​Array​Tuple是不同类型数据的集合(与Struct有相似之处),Array是相同数据的集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Tuple 类型可以将不同类型的变量放在一起
// 函数可以使用Tuple来返回多个值
fn tuple () {
let tup: (i32, f64, u8) = (500, 6.4, 1);
let tup = (500, 6.4, 1); // 编译器能够推断我们需要什么类型

// 解包
let (x, y, z) = tup;
// 也可以使用索引的形式访问
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = x.2;
} // Tuple end

// Array: 存放在栈中,当我们想把数据存放在栈里时十分有用
// 但是Array只能存放相同类型的变量
fn array () {
let a = [1, 2, 3, 4, 5];
let index = 2;

let element = a[index]; // 使用方括号来访问Array中的内容
println!("The value of element is {}", element);
}// Array end

表达式与语句

对表达式和语句的精确区分是Rust精准高效的一个体现。
Rust中的函数由一系列语句(statement)和一个可选的结尾表达式(expression)组成。与其他语言对表达式和语句的模糊对待不同,Rust是一个由表达式组成的编程语言,在语言设计中包含了大量的表达式的运用,因此在使用时需要多多注意。

  • Statement(语句):指的是一个执行了特定的操作但是没有返回值得指令
  • Expression(表达式):计算了一个值并将其返回
    1
    2
    3
    4
    5
    6
    let x = 6; // “6”是一个表达式

    let y = {
    let x = 3; // 表达式也可以是由`{}`
    x + 1 // 包围起来的代码块
    };

为了区分语句和表达式,可以通过末尾是否使用;来加以区分。

Rust控制流

Rust也提供了一些跟流控制相关的机制,通常我们接触到的就是if, while, for.

If表达式

Rust中也有If关键字,对条件进行判断,选择不同的执行路径,在这里我们并不是像C++一样称为if语句,而是if 表达式。值得注意的是,和C/C++、Python之类语言不同,if的条件必须是bool类型。并且由于if是一个表达式,我们可以通过if表达式的返回值来为变量赋值,这个操作和Python中的 x= a if condition else b​很类似。有Python编程经验的人一定非常熟悉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// basic usage
fn main() {
let number = 3;

// contition must be bool type
// if number {
// println!("Which is not allow in Rust!");
// }
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}

// multiple condition
fn main() {
let number = 6;

if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}

// if statement
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
//! we shouldn't use "six" here cause the type of `number` depends on condition
//! compiler will reject this behavior;
// "six"
};
println!("The value of number is: {}", number);
}

上面代码中展示了if的一些基本用法,注意到第37行的注释,使用if表达式为变量赋值,if的每一个分支的表达式返回值类型必须一样。

循环

Rust提供了3种类型的循环:loop, for, while

loop​ 表达式

​​loop​在Rust中充当着无限循环的角色,例如我们创建一个服务器,需要不断地从网卡中获取客户端发送的请求,loop在这种场景下经常被使用。另外可以使用loop加上break表达式跳出循环并且返回某个表达式,后面再详细地说。

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut counter = 0;

let result = loop { // result will be assigned with 20
counter += 1;
if counter == 10 {
break counter * 2;
}
};
assert_eq!(result, 20);
}

while表达式

while循环在其他编程语言中十分常见,用法也类似,一般用于可预测最终结果的循环。
while还有一个另外的用法,就是
while let Pattern = Expression这个使用方法就是当 Pattern=Expression这个条件满足的时候,就执行循环,当有一个条件不满足的时候就跳出循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// basic usage of while
fn main() {
let mut i = 0;

while i < 10 {
println!("hello");
i = i + 1;
}
}
// Predicate pattern loops
fn main() {
let mut x = vec![1, 2, 3];

while let Some(y) = x.pop() {
println!("y = {}", y);
}
}
// Outputs:
// y = 3
// y = 2
// y = 1

for 表达式

for表达式一般用来迭代某个容器,在Rust中这个是最常见的做法,也是for表达式出现最多的地方。

1
2
3
4
5
6
7
8
// usage of for
fn main() {
let mut sum = 0;
for n in 1..11 {
sum += n;
}
assert_eq!(sum, 55);
}

关于表达式

既然说到loopwhilefor都是表达式,那么他们返回什么值?这个需要分情况对待。

loop

loop表示无限循环。既然表示无限循环了,表示那么一般情况下loop的下一个表达式/语句是永远不会执行到的。除非和break表达式一起使用时才能够跳出循环,所以loopbreak一起使用时,能够用于从loop中返回值。

break表达式

break表达式放在loop这层级下来描述不是特别好,但是既然在loop中讲到了break,那么就一起将内容放在这里挤一挤。

和其他语言一样,break一般用于循环体中的跳出,它不只用于loop,还能用于whilefor中。一般程序执行到break以后,将会跳出循环体,whilefor天然就是loopifelse…和break的结合体。正由于这一点,在大家都是表达式这一点whileforloop表现得有一些不同。

1
2
3
4
5
6
7
8
9
10
11
12
// return value from loop
fn main() {
let mut count = 0;

let result = loop {
count += 1;
if count == 10 {
break count * 2;
}
};
assert_eq!(result, 20);
}

在这里, break后的表达式的值成为loop的返回值。所以result的值为20,类型为i32(编译器推断)。

值得注意的是, 我们可以在loop里面有多个if判断,然后有多个break表达式,但是这几个break表达式返回的类型必须一致,这样子编译器才能够确定result的类型。在break后也可以不带任何表达式,此时loop表达式返回的是unit type也就是 ​()​。也就是说loop表达式能够返回值的,表达式的叫法实至名归。

说到这里 whilefor也想给他们自己正名。上文说到的 “whilefor天然就是loopifelse…和break的结合体”,并且“break后如果不带任何的表达式,loop返回的是 unit type”。所以 whilefor 也是表达式,并且他们的返回值就是unit type我们可以看做编译器帮我们添加了一个后面不带表达式的break表达式,并且由于各个break表达式块的返回类型必须相等,因此我们在whilefor循环中就只能使用break而不能在后面添加任何的表达式,或者直接在​while​for程序块末尾使用非unit type 的表达式也是不被允许的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let mut x = vec![1,2,3];
let y = for n in x {
println!("n = {}", n);
//! Not allow: can only break value inside `loop`
// if n == 2 {
// break 2
// }

//! Not allow: expected (), found integral variable
// 2

//! Allow
// ()
};
println!("y = {:?}", y);
}

// Output:
// n = 3
// n = 2
// n = 2
// y = ()

上述代码只用for表达式的运行结果来展示,while表达式各位请自行验证。
至此,循环控制模块都是表达式这一点被证实了。

循环label:嵌套块跳转

C/C++中嵌套循环的跳出是一个比较麻烦的过程,Rust吸取了Java中类似的做法,支持给循环增加label​命名通过在break/continue背后指定label的形式跳出到指定的循环嵌套模块。'foo: loop { break 'foo; }, 'bar: while false {}, 'humbug: for _ in 0..0 {}

1
2
3
4
5
6
'outer: loop {
while true {
// -- code snip --
break 'outer;
}
}
Author: lisupy
Link: http://lisupy.github.io/2018/11/25/Rust基本类型和逻辑规则/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
支付宝打赏
微信打赏