在这里插入图片描述

在掌握了Rust的基础知识后,让我们继续深入探索这门语言更强大的特性。本文将带你了解结构体、枚举、模式匹配等核心概念,并展示如何在实际项目中运用它们。

第五章:使用结构体组织相关联的数据

5.1 定义和实例化结构体

结构体是Rust中创建自定义数据类型的主要方式,让我们能够将相关的数据组合在一起。

基本结构体定义:

// 定义结构体
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

// 实例化结构体
fn main() {
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    println!("用户邮箱:{}", user1.email);
    
    // 创建可变的结构体实例
    let mut user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotheruser"),
        active: true,
        sign_in_count: 1,
    };
    
    user2.email = String::from("newemail@example.com");
}

结构体更新语法:

let user3 = User {
    email: String::from("user3@example.com"),
    username: String::from("userthree"),
    ..user1  // 使用user1的其余字段值
};

5.2 方法语法

方法是在结构体上下文中定义的函数,第一个参数总是self

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

// 在impl块中定义方法
impl Rectangle {
    // 关联函数(静态方法)
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
    
    // 方法 - 借用self
    fn area(&self) -> u32 {
        self.width * self.height
    }
    
    // 方法 - 可变借用self
    fn resize(&mut self, width: u32, height: u32) {
        self.width = width;
        self.height = height;
    }
    
    // 方法 - 获取self
    fn consume(self) -> String {
        format!("消耗了{}x{}的矩形", self.width, self.height)
    }
    
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn main() {
    let mut rect = Rectangle {
        width: 30,
        height: 50,
    };
    
    println!("矩形面积:{}", rect.area());
    
    rect.resize(40, 60);
    println!("调整后面积:{}", rect.area());
    
    let rect2 = Rectangle {
        width: 20,
        height: 40,
    };
    
    println!("rect能否容纳rect2:{}", rect.can_hold(&rect2));
    
    let square = Rectangle::square(25);
    println!("正方形:{:?}", square);
}

第六章:枚举和模式匹配

6.1 定义枚举

枚举允许我们定义一个类型,其值可以是几个不同的变体之一。

// 基本枚举
enum IpAddrKind {
    V4,
    V6,
}

// 带数据的枚举
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

// 复杂枚举示例
enum Message {
    Quit,                       // 无关联数据
    Move { x: i32, y: i32 },    // 匿名结构体
    Write(String),              // 单个String
    ChangeColor(i32, i32, i32), // 三个i32值
}

// 为枚举定义方法
impl Message {
    fn call(&self) {
        // 方法实现
    }
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;
    
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));
    
    let msg = Message::Write(String::from("hello"));
    msg.call();
}

6.2 match控制流运算符

match是Rust中强大的控制流运算符,允许我们将值与一系列模式进行比较。

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    // ... 其他州
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("幸运的一分钱!");
            1
        }
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("来自{:?}州的25分硬币", state);
            25
        }
    }
}

// 匹配Option<T>
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let coin = Coin::Quarter(UsState::Alaska);
    println!("硬币价值:{}分", value_in_cents(coin));
    
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);
    
    println!("six: {:?}, none: {:?}", six, none);
}

6.3 if let简洁控制流

if let语法让我们可以处理只关心一种模式而忽略其他模式的情况。

fn main() {
    let some_u8_value = Some(3u8);
    
    // 使用match
    match some_u8_value {
        Some(3) => println!("三"),
        _ => (),
    }
    
    // 使用if let - 更简洁
    if let Some(3) = some_u8_value {
        println!("三");
    }
    
    // if let else 示例
    let mut count = 0;
    let coin = Coin::Quarter(UsState::Alaska);
    
    if let Coin::Quarter(state) = coin {
        println!("来自{:?}州的25分硬币", state);
    } else {
        count += 1;
    }
}

第七章:模块系统

7.1 包和Crate

项目组织:

my_project/
├── Cargo.toml
└── src/
    ├── main.rs
    ├── lib.rs
    └── front_of_house/
        ├── hosting.rs
        └── serving.rs

7.2 定义模块控制作用域和私有性

src/lib.rs:

// 模块声明
mod front_of_house;

// 使用pub关键字使模块公开
pub mod back_of_house {
    // 公开枚举的所有变体自动成为公开的
    pub enum Appetizer {
        Soup,
        Salad,
    }
    
    // 结构体字段默认是私有的
    pub struct Breakfast {
        pub toast: String,      // 公开字段
        seasonal_fruit: String, // 私有字段
    }
    
    impl Breakfast {
        // 关联函数
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

// 使用use关键字引入路径到作用域
use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    // 绝对路径
    crate::front_of_house::hosting::add_to_waitlist();
    
    // 相对路径
    front_of_house::hosting::add_to_waitlist();
    
    // 使用use引入的路径
    hosting::add_to_waitlist();
    
    // 点餐示例
    let mut meal = back_of_house::Breakfast::summer("Rye");
    meal.toast = String::from("Wheat");
    println!("我要{}吐司", meal.toast);
    
    let order1 = back_of_house::Appetizer::Soup;
    let order2 = back_of_house::Appetizer::Salad;
}

src/front_of_house/hosting.rs:

pub fn add_to_waitlist() {}
fn seat_at_table() {}

src/front_of_house/serving.rs:

fn take_order() {}
fn serve_order() {}
fn take_payment() {}

第八章:常见集合

8.1 向量(Vector)

fn main() {
    // 创建向量
    let mut v: Vec<i32> = Vec::new();
    let v2 = vec![1, 2, 3]; // 使用宏创建
    
    // 添加元素
    v.push(5);
    v.push(6);
    v.push(7);
    
    // 访问元素
    let third: &i32 = &v[2];
    println!("第三个元素是:{}", third);
    
    match v.get(2) {
        Some(third) => println!("第三个元素是:{}", third),
        None => println!("没有第三个元素"),
    }
    
    // 遍历向量
    for i in &v {
        println!("{}", i);
    }
    
    // 遍历并修改
    for i in &mut v {
        *i += 50;
    }
    
    // 使用枚举存储多种类型
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }
    
    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
}

8.2 字符串(String)

fn main() {
    // 创建字符串
    let mut s = String::new();
    let s1 = "初始内容".to_string();
    let s2 = String::from("另一个字符串");
    
    // 更新字符串
    s.push_str("foo");
    s.push('b');
    
    let s3 = s1 + &s2; // 注意:s1被移动了,不能再使用
    
    // 使用format!宏
    let s4 = format!("{}-{}-{}", "tic", "tac", "toe");
    
    // 字符串切片
    let hello = "Здравствуйте";
    let s5 = &hello[0..4]; // 前4个字节
    
    // 遍历字符串
    for c in "नमस्ते".chars() {
        println!("{}", c);
    }
    
    for b in "नमस्ते".bytes() {
        println!("{}", b);
    }
}

8.3 哈希映射(HashMap)

use std::collections::HashMap;

fn main() {
    // 创建哈希映射
    let mut scores = HashMap::new();
    
    // 插入值
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    
    // 访问值
    let team_name = String::from("Blue");
    let score = scores.get(&team_name);
    
    // 遍历
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
    
    // 更新值
    scores.insert(String::from("Blue"), 25); // 覆盖
    
    // 只在键不存在时插入
    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Red")).or_insert(0);
    
    // 根据旧值更新
    let text = "hello world wonderful world";
    let mut map = HashMap::new();
    
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }
    
    println!("{:?}", map);
}

第九章:错误处理

9.1 不可恢复的错误与panic!

fn main() {
    // 恐慌宏 - 程序会终止执行
    // panic!("崩溃了!");
    
    // 在Cargo.toml中设置 panic = 'abort' 可以立即终止程序
}

9.2 可恢复的错误与Result

use std::fs::File;
use std::io::{self, Read};

// 传播错误的简单方式
fn read_username_from_file() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}

// 更复杂的错误处理
fn divide(x: f64, y: f64) -> Result<f64, String> {
    if y == 0.0 {
        Err(String::from("除数不能为零"))
    } else {
        Ok(x / y)
    }
}

fn main() {
    // 使用match处理Result
    let result = divide(10.0, 2.0);
    match result {
        Ok(value) => println!("结果是:{}", value),
        Err(e) => println!("错误:{}", e),
    }
    
    // 使用unwrap和expect
    let f = File::open("hello.txt").unwrap(); // 错误时panic
    let f = File::open("hello.txt").expect("无法打开文件"); // 带自定义消息的panic
    
    // 使用?运算符传播错误
    match read_username_from_file() {
        Ok(username) => println!("用户名:{}", username),
        Err(e) => println!("读取文件错误:{}", e),
    }
}

实战项目:构建简单的待办事项应用

让我们用学到的知识构建一个简单的命令行待办事项应用:

Cargo.toml:

[package]
name = "todo_app"
version = "0.1.0"
edition = "2021"

[dependencies]

src/main.rs:

use std::collections::HashMap;
use std::io;

#[derive(Debug)]
enum TaskStatus {
    Pending,
    Completed,
}

struct TodoList {
    tasks: HashMap<u32, (String, TaskStatus)>,
    next_id: u32,
}

impl TodoList {
    fn new() -> TodoList {
        TodoList {
            tasks: HashMap::new(),
            next_id: 1,
        }
    }
    
    fn add_task(&mut self, description: String) -> u32 {
        let id = self.next_id;
        self.tasks.insert(id, (description, TaskStatus::Pending));
        self.next_id += 1;
        id
    }
    
    fn complete_task(&mut self, id: u32) -> Result<(), String> {
        match self.tasks.get_mut(&id) {
            Some((_, status)) => {
                *status = TaskStatus::Completed;
                Ok(())
            }
            None => Err(format!("任务ID {} 不存在", id)),
        }
    }
    
    fn list_tasks(&self) {
        if self.tasks.is_empty() {
            println!("没有待办事项");
            return;
        }
        
        for (id, (description, status)) in &self.tasks {
            let status_str = match status {
                TaskStatus::Pending => "待完成",
                TaskStatus::Completed => "已完成",
            };
            println!("{}: {} [{}]", id, description, status_str);
        }
    }
    
    fn remove_task(&mut self, id: u32) -> Result<(), String> {
        match self.tasks.remove(&id) {
            Some(_) => Ok(()),
            None => Err(format!("任务ID {} 不存在", id)),
        }
    }
}

fn main() {
    let mut todo_list = TodoList::new();
    
    loop {
        println!("\n=== 待办事项应用 ===");
        println!("1. 添加任务");
        println!("2. 完成任务");
        println!("3. 列出任务");
        println!("4. 删除任务");
        println!("5. 退出");
        println!("请选择操作:");
        
        let mut choice = String::new();
        io::stdin()
            .read_line(&mut choice)
            .expect("读取输入失败");
            
        match choice.trim() {
            "1" => {
                println!("请输入任务描述:");
                let mut description = String::new();
                io::stdin()
                    .read_line(&mut description)
                    .expect("读取输入失败");
                    
                let id = todo_list.add_task(description.trim().to_string());
                println!("已添加任务,ID: {}", id);
            }
            "2" => {
                println!("请输入要完成的任务ID:");
                let mut id_input = String::new();
                io::stdin()
                    .read_line(&mut id_input)
                    .expect("读取输入失败");
                    
                match id_input.trim().parse() {
                    Ok(id) => {
                        match todo_list.complete_task(id) {
                            Ok(()) => println!("任务已完成"),
                            Err(e) => println!("错误:{}", e),
                        }
                    }
                    Err(_) => println!("无效的ID"),
                }
            }
            "3" => {
                todo_list.list_tasks();
            }
            "4" => {
                println!("请输入要删除的任务ID:");
                let mut id_input = String::new();
                io::stdin()
                    .read_line(&mut id_input)
                    .expect("读取输入失败");
                    
                match id_input.trim().parse() {
                    Ok(id) => {
                        match todo_list.remove_task(id) {
                            Ok(()) => println!("任务已删除"),
                            Err(e) => println!("错误:{}", e),
                        }
                    }
                    Err(_) => println!("无效的ID"),
                }
            }
            "5" => {
                println!("再见!");
                break;
            }
            _ => println!("无效的选择,请重试"),
        }
    }
}

记住,Rust的学习是一个渐进的过程。每个新概念都可能需要时间来完全理解,但这种投资最终会得到丰厚的回报。继续编码,享受Rust带来的安全性和性能优势!

继续你的Rust之旅!🚀


版权声明:本教程仅供学习使用,转载请注明出处。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐