从零学Rust - 函数与流程控制

106 阅读4分钟

语句与表达式

rust 的函数体由语句和表达式组成。

fn add_with_extra(x: i32, y: i32) -> i32 {
    let x = x + 1; // 语句
    let y = y + 5; // 语句
    x + y // 表达式
}

语句不返回值,而表达式返回值。

// 和很多常见语言中不一样,比如 let x = y = 6 在 js 可以同时给 x,y 赋值
// 由于 let 是语句,没有返回值,因此不能将 let 语句赋值给其它值
let x = (let y = 8);

表达式会进行求值,然后返回一个值,表达式不能包含分号。常见如下:

fn main {
  // 函数调用,没有返回值会隐式地返回一个 () 
  hello_world(); 
  // 宏调用,返回同函数
  println!("hello world"); 
  // 10 + 6 数学运算表达式
  let a = 10 + 6; 
  // 语句块表达式,赋值给变量
  let x = {
    let x = 10;
    x + 1 // 最后一行是表达式,返回了 x + 1 的值,不能以分号结尾
  };
  // if 语句块也是一个表达式,类似三元运算符
  let z = if x % 2 == 1 { "odd" } else { "even" };
}

函数声明与使用

函数的签名包括:

  • fn关键字
  • 函数名称:命名规范为 snake_case
  • 参数 parameters
  • 如果有返回值:函数体前加上 -> 类型
  • 返回值可以通过 return 返回,或是最后一个表达式
fn main() {
    hello_word();
    another_function(100, 'h'); // 为函数的形参提供具体值(实参)
  
    // this function takes 2 arguments but 1 argument was supplied
    another_function(100);
  
    let a = plus_five(10);
    println!("{a}");
}
// 这个定义放 main 之前之后都行,不关心定义的位置
// 只要函数被调用时出现在调用之处可见的作用域内就行
fn hello_word() {
    println!("hello word")
}
​
// 函数签名中,必须显式声明每个参数的类型
fn another_function(x: i32, unit_label: char) {
    println!("The value of x is: {x} {unit_label}");
}
​
// 有返回值,需要在函数体前加上类型
fn plus_five(x:i32) -> i32 {
    if x > 10 {
        return 100; // 通过 return 返回
    }
    x + 5 // 最后一个表达式作为返回值
}
​
// 特殊的返回值
// 显式的声明返回单元类型,其实就是没有返回值
fn zero() -> () {}
// 永不返回的发散函数
fn forever() -> ! {
  loop {};
}

流程控制

if else 条件分支

和其他语言的相差不大,ifelseelse if

// 括号可加可不加,因为括号也是包含一个表达式
// == 只有全等
if condition1 {
    // A...
} else if condition1 {
    // B...
} else {}

因为if 是一个表达式,可以在let 语句右侧使用它

let condition = true;
let number = if condition { 5 } else { 6 };
// error[E0308]: `if` and `else` have incompatible types
// 值的类型必须相同
let number = if condition { 5 } else { "six" };

loop 循环

fn main() {
    // 一直执行,不会返回值,即发散函数 fn main -> ! {}
    loop {
        println!("again!");
    }
  
    // 如果上面的代码不注释,下面的代码会提示:unreachable statement 
  
    // 通过 break 结束循环,通过 continue 结束当前循环
    let mut x = 1;
    loop {
        x += 1;
        if x == 2 {
          continue; // 不会打印 x = 2
        }
        println!("x = {x}");
        if x > 5 {
            println!("end {x}"); // end 6
            break;
        }
    }
  
    // 通过 break 结束循环并返回值
    let mut a = 1;
    let b = loop {
      a += 1;
      if a > 9 {
        break a * 10;
      }
    };
    println!("{b}") // 100
}

嵌套的 loop,可以通过循环标签进行标记,并通过 break 结束。

fn main() {
    // 通过 break 结束循环,通过 continue 结束当前循环
    let mut a = 1;
    let mut b = 1;
    'label1: loop {
        a += 1;
        'label2: loop {
            b += 2;
            println!("a = {a} b = {b}");
            if a + b == 5 {
                b += 1;
                println!("b = {b}");
                break 'label2;
            }
            if a + b > 5 {
                break 'label1;
            }
        }
    }
}
// a = 2 b = 3
// b = 4
// a = 3 b = 6

while 循环

和其他语言中差不多,while 消除了很多使用 loopifelsebreak 时所必须的嵌套。

// 使用 while 循环遍历集合中的元素
fn main() {
    let a = [1, 2, 3];
    let mut index = 0;
    
    // 如果这里改成4,出现数组访问越界
    // thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 3'
    while index < 3 {
        println!("x = {}", a[index]);
        index += 1;
    }
}
// x = 1
// x = 2
// x = 3

使用while 如果索引长度或测试条件不正确会导致程序 panic,可以用 for 循环替代。

for 循环

forin 组合

let a = [1, 2, 3];
​
for element in a {
    println!("a = {element}");
}

Rust 提供了一个非常简洁的方式,用来生成连续的数值,即序列(Range)

  • 1..5:生成从 1 到 4 的连续数字
  • 1..=5:生成从 1 到 5 的连续数字
  • 'a'..='z': 生成 a 到 z 的字母

序列只允许用于数字或字符类型,原因是:它们可以连续。

fn main() {
    // 使用 rev,用来反转 range
    for number in (1..4).rev() {
        println!("{number}!");
    }
    // 3 2 1
}