Skip to content
Go back

small_exercise250604_rust_practice

斐波那契数列: 基础类型控制流

fn main() {
    println!("fib(5) = {}", fib(5));
}

fn fib(n: u32) -> u32 {
    match n {
        0 => 0,
        1 => 1,
        _ => fib(n - 1) + fib(n - 2),
    }
}

对比Java: Rust的match类似Java的switch但更强大(强制覆盖所有情况) 类型标注n: u32(无符号32位整数)替代Java的int


定义几何图形和面积计算: 结构体与枚举

fn main() {
    let circle = Shape::Circle {
        center: Point {x: 0.0, y: 0.0}, 
        radius: 5.0,
    };
    
    println!("Circle area: {}", circle.area());
}
struct Point {x: f64, y: f64}

enum Shape {
    Circle {center: Point, radius: f64},
    Rectangle {top_left: Point, bottom_right: Point},
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            // 如果是圆形
            Shape::Circle {radius, ..} => std::f64::consts::PI * radius * radius,
            // 如果是四边形.
            Shape::Rectangle {top_left, bottom_right} => {
                let width = (bottom_right.x - top_left.x).abs();
                let height = (top_left.y - bottom_right.y).abs();

                width * height
            }
        }
    }
}

Circle area: 78.53981633974483 对比Java: struct ≈ Java的POJO(但不可变默认) enum 可携带数据(类似Java的sealed class + records) impl 块为类型添加方法(类似Java的类方法)


使用迭代器处理数据: 集合操作(Vec与HashMap)

use std::collections::HashMap;

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let doubled: Vec<i32> = numbers
        .iter()
        .map(|x| x*2)
        .filter(|x| x % 3 != 0) 
        .collect();
    
    println!("{:?}", doubled);
    
    let mut scores = HashMap::new();
    scores.insert("Blue", 10);
    // 类似Java的computeIfAbsent
    scores.entry("Green").or_insert(50);
    
    for (key, value) in &scores {  // 使用 &scores 而不是 scores 来遍历 HashMap 是出于所有权和借用规则的考虑
        println!("{}: {}", key, value);
    }



为什么使用 &scores
所有权保留:
for (key, value) in &scores 表示你是在借用(borrowHashMap
遍历后 scores 的所有权仍然保留在作用域中
后续你仍然可以修改或访问 scores


避免所有权转移:
如果使用 for (key, value) in scores
// 这会转移所有权!
for (key, value) in scores {  // ❌ 消耗了 scores 的所有权
    println!("{}: {}", key, value);
}

循环后 scores 会被移动(moved),无法再使用
内存效率:
&scores 创建的是对 HashMap 的引用,不会复制实际数据
直接使用 scores 会导致整个 HashMap 的所有权被转移到循环中

}

输出样例

[2, 4, 8, 10] Green: 50 Blue: 10

对比Java: vec![] 宏 ≈ ArrayList 迭代器链式调用(类似Java Stream API) HashMap::entry() 提供安全更新(避免NPE)


错误示例

for (key, value) in scores { // 所有权被转移
    println!("{}: {}", key, value);
}

// 这里不能再使用 scores!
scores.insert("Red", 20); // ❌ 编译错误:value borrowed here after move

只有当你确定遍历后不再需要这个集合时:

let scores = HashMap::from([("Blue", 10), ("Green", 50)]);

// 消耗性遍历(之后 scores 不再存在)
for (key, value) in scores {
    println!("Consuming: {} -> {}", key, value);
}
// 这里 scores 已失效

最佳实践

只读访问 → 用 &collection 修改内容 → 用 &mut collection 集合不再需要 → 直接使用 collection


安全解析用户输入:错误处理(Option与Result)

fn main() {
    let inputs = vec!["42", "aaa"];
    
    for input in inputs {
        match parse_input(input) {
            Ok(num) => println!("Parsed: {:?}", num),
            Err(err) => println!("Error: {:?}", err),
        }
    }
}

fn parse_input(input: &str) ->Result<i32, String> {
    input.parse::<i32>().map_err(|e| format!("Parse error: {:?}", e))
}


::<i32>(Turbofish 语法)
作用:显式指定泛型类型
解析:
input.parse() 是一个泛型方法,它需要知道要解析成什么类型(如 i32, f64, String 等)
::<i32> 明确告诉编译器:“将输入解析为 i32 类型”
等价于显式类型标注:
let num: i32 = input.parse()?; // 通过类型推断实现
为什么需要:
当编译器无法自动推断类型时(如复杂嵌套场景),必须用 ::<T> 显式指定类型。


map_err
作用:转换 Result 中的错误类型
工作机制:
如果 ResultOk(value) → 直接返回原值
如果 ResultErr(e) → 应用给定函数转换错误 e
在代码中的含义:
.map_err(|e| format!("Parse error: {:?}", e))
parse() 的原生错误(通常是 ParseIntError)转换成自定义字符串错误
最终错误类型从 ParseIntError 变为 String

输出样例

Parsed: 42 Error: “Parse error: ParseIntError { kind: InvalidDigit }”

对比Java: Result<T, E> ≈ 显式受检异常(强制处理) Option 替代null(编译时防空指针) map_err() 类似Java的异常转换


理解移动/借用规则:所有权与借用

fn main() {
    let a = Data {value: "a".to_string()};
    consume(a); // 所有权转义
    // println!("{}", a.value); // 编译错误!a已失效

    let b = Data {value: "b".to_string()};
    borrow(&b); // 借用,只读
    println!("Still own: {}", b.value);
}

struct Data {value: String}

fn consume(data: Data) {
    println!("Consumed: {}", data.value);
} // data被销毁

fn borrow(data: &Data) {
    println!("Borrowed: {}", data.value);
}

输出样例

Consumed: a Borrowed: b Still own: b

借用检查器:编译器强制避免数据竞争(&只读引用,&mut可变引用)



// 解构枚举 + if let 语法糖
fn print_shape(shape: &Shape) {
    if let Shape::Circle { radius, .. } = shape {
        println!("Circle radius: {}", radius);
    }
}

// 使用 ? 操作符简化错误传播
fn read_file() -> Result<String, std::io::Error> {
    let content = std::fs::read_to_string("file.txt")?; // 自动返回错误
    Ok(content)
}

Share this post on: