XiaoboTalk

语法篇-面向表达式

Rust 在编程范式上基本不做限制,Rust 没有继承,但是依然可以实现面向对象 (OOP),也支持函数式编程(FP),也支持泛型。在语法上 Rust 更多的借鉴了函数式编程的概念(FP)。可以认为,Rust 是一门面向表达式的语句,由于表达式总会返回某种类型的值,所以 Rust 是面向类型推断的。

表达式和语句

这是两个重要的编程语言的概念,表达式(expression): 一定数量的符号的集合,通常产生一个数学计算。简单的说,表达式总会产生(return)一个或一组值
12; // 产生一个值 12 2 + 3; // 产生一个值: 5 ; // 返回一个 ()
语句的概念比较泛化,维基百科的解释:
在计算机编程语言中,一个最小的独立的命令式编程语句单元。或者是一个包含了若干上述单元的程序。语句可以包含表达式。
常见的语句有:
1、声明语句:
use std::io::{Result}
2、流程控制语句
3、表达式语句
4、宏语句

Rust 中的两种重要表达式

从宏观上看,Rust 中有两大类表达式:
  • ; 分号表达式
  • {} 块表达式
; 分号在其他语言中一般都作为分隔符,但是在 Rust 中,; 有时候是一个表达式。
fn bar() { ; // 返回单元类型:() }
上边的 Rust 函数 bar ,在可以编译通过的,因为,当; 处在块表达式{} 最后一行的时候,会作为表达式,返回 () 单元类型,单元类型实际上是一个空元组。上边的代码也可以写为:
fn bar() -> () { ; }
当然也可以用 return 来显式的返回:
fn bar() -> () { return (); }
块表达式: 由于表达式,总要返回一个值,所以块表达式也遵循这个规则。上边的代码,bar() 的函数体,就是一个块表达式,返回了一个单元类型;如果块表达式最后一行没有; 分号表达式,那么最后一行的值,会被直接当做返回值,所以上边的代码还可以写作:
fn bar() -> () { () }
类似的,如果需要具体返回某种类型,可写作:
fn bar() -> i32 { let a = 12; let b = 13; a + b // 注意没有分号 }

Rust 中的流程控制也是表达式:

再来看一段代码:
fn bar(a: i32) { // 返回 () if a % 2 == 0 { println!("even") // println! 宏本身会返回单元类型 () } else { println!("odd") } }
假设我们设定,偶数返回 0,奇数返回 1:
fn bar(a: i32) -> i32 { let res = if a % 2 == 0 { 0 } else { 1 }; res } fn main() { println!("{}", bar(5)); // 输出 1 }
很明显,if 可以返回一个值,因为 if 后都是块表达式。

基于表达式的类型一致性

由于 Rust 是面向表达式的,所以,可以根据每个表达式的返回值,确定类型,并且将类型传递下去:
fn bar(a: i32) -> i32 { let res = if a % 2 == 0 { 0 } else { 1 }; res }
bar 是最外层的块表达式,需要一个 i32 的返回值,内部,if..else 的每个子句都是表达式,所以可以进行编译限制,让每个子句表达式都返回相同的类型。如果if..else 的某个子句表达式返回值不是 i32 就会产生编译错误:
fn bar(a: i32) -> i32 { let res = if a % 2 == 0 { 0 } else { "a" // Error,编译报错 }; res }