斐波那契数列: 基础类型控制流
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 表示你是在借用(borrow)HashMap
遍历后 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
错误示例
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 中的错误类型
工作机制:
如果 Result 是 Ok(value) → 直接返回原值
如果 Result 是 Err(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
理解移动/借用规则:所有权与借用
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)
}