iT邦幫忙

2025 iThome 鐵人賽

DAY 5
0
Rust

30天Rust從零到全端系列 第 5

Day 5: 控制流程進階與錯誤處理

  • 分享至 

  • xImage
  •  

前言

Hi 大家,昨天我們學習了基本的函式和流程控制,今天我們來深入探討更進階的控制流程概念,包括迴圈的進階用法、錯誤處理的基礎,以及如何在實際專案中運用這些概念。這些在軟體開發中特別是在處理使用者輸入和系統穩定性方面十分重要。

迴圈進階技巧

迴圈控制與標籤

fn main() {
    // 巢狀迴圈與標籤控制
    let mut count = 0;
    
    'counting_up: loop {
        println!("count = {}", count);
        let mut remaining = 10;
        
        loop {
            println!("remaining = {}", remaining);
            if remaining == 9 {
                break; // 只跳出內層迴圈
            }
            if count == 2 {
                break 'counting_up; // 跳出外層迴圈
            }
            remaining -= 1;
        }
        
        count += 1;
    }
    println!("結束計數,count = {}", count);
}

迭代器與收集

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    
    // 過濾偶數並平方
    let even_squares: Vec<i32> = numbers
        .iter()
        .filter(|&&x| x % 2 == 0)
        .map(|&x| x * x)
        .collect();
    
    println!("偶數的平方: {:?}", even_squares);
    
    // 使用 enumerate 取得索引
    for (index, value) in numbers.iter().enumerate() {
        if index % 2 == 0 {
            println!("索引 {} 的值: {}", index, value);
        }
    }
    
    // 範圍與步長
    for i in (0..10).step_by(2) {
        println!("步長為 2: {}", i);
    }
}

錯誤處理基礎

Option 類型

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    
    // 安全的陣列存取
    match get_element(&numbers, 2) {
        Some(value) => println!("找到值: {}", value),
        None => println!("索引超出範圍"),
    }
    
    // 使用 if let 簡化
    if let Some(value) = get_element(&numbers, 10) {
        println!("找到值: {}", value);
    } else {
        println!("沒有找到值");
    }
    
    // 連鎖操作
    let result = get_element(&numbers, 1)
        .map(|x| x * 2)
        .unwrap_or(0);
    
    println!("處理後的結果: {}", result);
}

fn get_element(vec: &Vec<i32>, index: usize) -> Option<i32> {
    if index < vec.len() {
        Some(vec[index])
    } else {
        None
    }
}

Result 類型基礎

fn main() {
    // 字串轉數字的錯誤處理
    let inputs = ["42", "abc", "123", "xyz"];
    
    for input in inputs {
        match parse_number(input) {
            Ok(number) => println!("解析成功: {} -> {}", input, number),
            Err(error) => println!("解析失敗: {}", error),
        }
    }
    
    // 使用 unwrap_or 提供預設值
    let number1 = parse_number("123").unwrap_or(0);
    let number2 = parse_number("abc").unwrap_or(0);
    
    println!("數字1: {}, 數字2: {}", number1, number2);
}

fn parse_number(input: &str) -> Result<i32, String> {
    match input.parse::<i32>() {
        Ok(num) => Ok(num),
        Err(_) => Err(format!("無法將 '{}' 轉換為數字", input)),
    }
}

進階模式匹配

解構與綁定

fn main() {
    let point = (3, 5);
    
    match point {
        (0, 0) => println!("原點"),
        (x, 0) => println!("在 X 軸上,x = {}", x),
        (0, y) => println!("在 Y 軸上,y = {}", y),
        (x, y) if x == y => println!("在對角線上: ({}, {})", x, y),
        (x, y) => println!("其他位置: ({}, {})", x, y),
    }
    
    // 解構陣列
    let array = [1, 2, 3, 4, 5];
    
    match array {
        [1, second, .., last] => {
            println!("第一個是 1,第二個是 {},最後一個是 {}", second, last);
        }
        [first, .., last] => {
            println!("第一個是 {},最後一個是 {}", first, last);
        }
        _ => println!("其他模式"),
    }
}

實際練習:學生成績管理系統

use std::io;

#[derive(Debug, Clone)]
struct Student {
    name: String,
    scores: Vec<i32>,
}

#[derive(Debug)]
enum GradeLevel {
    Excellent,  // 90-100
    Good,       // 80-89
    Average,    // 70-79
    NeedsImprovement, // 60-69
    Failing,    // 0-59
}

fn main() {
    println!("=== 學生成績管理系統 ===");
    let mut students = Vec::new();
    
    loop {
        println!("\n選擇操作:");
        println!("1. 新增學生");
        println!("2. 輸入成績");
        println!("3. 查看成績報告");
        println!("4. 離開");
        
        let choice = get_user_choice();
        
        match choice {
            1 => add_student(&mut students),
            2 => add_score(&mut students),
            3 => show_report(&students),
            4 => {
                println!("謝謝使用!");
                break;
            }
            _ => println!("無效選擇,請重新輸入"),
        }
    }
}

fn get_user_choice() -> i32 {
    loop {
        print!("請輸入選擇 (1-4): ");
        
        let mut input = String::new();
        match io::stdin().read_line(&mut input) {
            Ok(_) => {
                match input.trim().parse::<i32>() {
                    Ok(num) if num >= 1 && num <= 4 => return num,
                    _ => println!("請輸入 1 到 4 之間的數字"),
                }
            }
            Err(_) => println!("讀取輸入時發生錯誤"),
        }
    }
}

fn add_student(students: &mut Vec<Student>) {
    println!("請輸入學生姓名:");
    
    let mut name = String::new();
    match io::stdin().read_line(&mut name) {
        Ok(_) => {
            let name = name.trim().to_string();
            if !name.is_empty() {
                students.push(Student {
                    name: name.clone(),
                    scores: Vec::new(),
                });
                println!("已新增學生: {}", name);
            } else {
                println!("姓名不能為空");
            }
        }
        Err(_) => println!("讀取姓名時發生錯誤"),
    }
}

fn add_score(students: &mut Vec<Student>) {
    if students.is_empty() {
        println!("目前沒有學生,請先新增學生");
        return;
    }
    
    // 顯示學生列表
    println!("選擇學生:");
    for (index, student) in students.iter().enumerate() {
        println!("{}. {}", index + 1, student.name);
    }
    
    let student_index = loop {
        println!("請輸入學生編號:");
        let mut input = String::new();
        match io::stdin().read_line(&mut input) {
            Ok(_) => {
                match input.trim().parse::<usize>() {
                    Ok(num) if num >= 1 && num <= students.len() => {
                        break num - 1;
                    }
                    _ => println!("請輸入有效的學生編號"),
                }
            }
            Err(_) => println!("讀取輸入時發生錯誤"),
        }
    };
    
    // 輸入成績
    let score = loop {
        println!("請輸入成績 (0-100):");
        let mut input = String::new();
        match io::stdin().read_line(&mut input) {
            Ok(_) => {
                match input.trim().parse::<i32>() {
                    Ok(num) if num >= 0 && num <= 100 => break num,
                    _ => println!("請輸入 0 到 100 之間的成績"),
                }
            }
            Err(_) => println!("讀取成績時發生錯誤"),
        }
    };
    
    students[student_index].scores.push(score);
    println!("已為 {} 新增成績: {}", students[student_index].name, score);
}

fn show_report(students: &Vec<Student>) {
    if students.is_empty() {
        println!("目前沒有學生資料");
        return;
    }
    
    println!("\n=== 成績報告 ===");
    
    for student in students {
        println!("\n學生: {}", student.name);
        
        if student.scores.is_empty() {
            println!("  尚無成績記錄");
            continue;
        }
        
        let average = calculate_average(&student.scores);
        let grade_level = determine_grade_level(average);
        
        println!("  成績: {:?}", student.scores);
        println!("  平均: {:.1}", average);
        println!("  等級: {:?}", grade_level);
        
        // 顯示成績統計
        if let Some(highest) = student.scores.iter().max() {
            println!("  最高分: {}", highest);
        }
        
        if let Some(lowest) = student.scores.iter().min() {
            println!("  最低分: {}", lowest);
        }
    }
    
    // 班級統計
    show_class_statistics(students);
}

fn calculate_average(scores: &Vec<i32>) -> f64 {
    if scores.is_empty() {
        return 0.0;
    }
    
    let sum: i32 = scores.iter().sum();
    sum as f64 / scores.len() as f64
}

fn determine_grade_level(average: f64) -> GradeLevel {
    match average as i32 {
        90..=100 => GradeLevel::Excellent,
        80..=89 => GradeLevel::Good,
        70..=79 => GradeLevel::Average,
        60..=69 => GradeLevel::NeedsImprovement,
        _ => GradeLevel::Failing,
    }
}

fn show_class_statistics(students: &Vec<Student>) {
    let students_with_scores: Vec<&Student> = students
        .iter()
        .filter(|student| !student.scores.is_empty())
        .collect();
    
    if students_with_scores.is_empty() {
        return;
    }
    
    println!("\n=== 班級統計 ===");
    
    let all_scores: Vec<i32> = students_with_scores
        .iter()
        .flat_map(|student| &student.scores)
        .cloned()
        .collect();
    
    let class_average = calculate_average(&all_scores);
    println!("班級平均: {:.1}", class_average);
    
    // 等級分布
    let mut grade_counts = [0; 5]; // [優秀, 良好, 普通, 需改進, 不及格]
    
    for student in &students_with_scores {
        let avg = calculate_average(&student.scores);
        let grade_index = match determine_grade_level(avg) {
            GradeLevel::Excellent => 0,
            GradeLevel::Good => 1,
            GradeLevel::Average => 2,
            GradeLevel::NeedsImprovement => 3,
            GradeLevel::Failing => 4,
        };
        grade_counts[grade_index] += 1;
    }
    
    let grade_names = ["優秀", "良好", "普通", "需改進", "不及格"];
    println!("等級分布:");
    for (i, &count) in grade_counts.iter().enumerate() {
        if count > 0 {
            println!("  {}: {} 人", grade_names[i], count);
        }
    }
}

Follow-up

  1. 修改程式,加入刪除學生的功能
  2. 實作成績排序功能
  3. 加入科目分類(國文、數學、英文等)

總結

今天我們學習了:

  • 迴圈的進階控制技巧
  • Option 和 Result 型別的基礎用法
  • 進階的模式匹配技巧
  • 實際專案中的錯誤處理

上一篇
Day 4: 函式與程式流程控制
下一篇
Day 6: 迴圈進階與迭代器
系列文
30天Rust從零到全端15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言