10分钟Solana-性能web3-2.4 Rust 编程基础三:结构体、枚举、错误处理与集合

欢迎订阅专栏10分钟Solana-性能web3

Rust 编程基础三:结构体、枚举、错误处理与集合

在掌握了所有权与生命周期之后,我们将进入 Rust 的面向对象风格编程。本篇涵盖结构体、枚举、模式匹配、错误处理、泛型、Trait 和常用集合类型,并最终组合成一个"代币账户管理"的实战示例。


一、结构体与方法

结构体是 Rust 中自定义数据类型的主要方式,类似于 Solidity 中的 struct

1. 定义与实例化

rust 复制代码
// 定义结构体
struct User {
    username: String,
    email: String,
    active: bool,
    sign_in_count: u64,
}

fn main() {
    // 创建实例
    let user1 = User {
        username: String::from("alice"),
        email: String::from("alice@example.com"),
        active: true,
        sign_in_count: 1,
    };

    // 访问字段
    println!("用户名: {}", user1.username);

    // 可变实例
    let mut user2 = User {
        username: String::from("bob"),
        email: String::from("bob@example.com"),
        active: true,
        sign_in_count: 1,
    };
    user2.email = String::from("bob@new.com");

    // 结构体更新语法(类似 JS 的展开)
    let user3 = User {
        username: String::from("charlie"),
        ..user2 // 剩余字段从 user2 复制
    };
}

注意 :如果字段包含 String 等非 Copy 类型,更新时可能发生所有权转移(如 user2 中的 email 可能被移动到 user3)。

2. 元组结构体 (Tuple Struct)

没有字段名,只有类型。

rust 复制代码
struct Color(u8, u8, u8);
struct Point(i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0);
}

3. 方法 (Method)

方法定义在 impl 块中,第一个参数是 self(或 &self&mut self)。

rust 复制代码
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 不可变借用方法
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 可变借用方法
    fn scale(&mut self, factor: u32) {
        self.width *= factor;
        self.height *= factor;
    }

    // 获取所有权的方法(较少使用)
    fn into_tuple(self) -> (u32, u32) {
        (self.width, self.height)
    }
}

fn main() {
    let mut rect = Rectangle { width: 10, height: 20 };
    println!("面积: {}", rect.area());
    rect.scale(2);
    println!("缩放后面积: {}", rect.area());

    let tuple = rect.into_tuple(); // rect 被移动,后续不可再使用
    // println!("{}", rect.width); // 错误
}

4. 关联函数 (Associated Function)

不带 self 参数的函数,类似于 Solidity 中的 static 函数,通常用于构造器。

rust 复制代码
impl Rectangle {
    fn new(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }

    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}

fn main() {
    let rect = Rectangle::new(10, 20);
    let square = Rectangle::square(15);
}

二、枚举与模式匹配

枚举是 Rust 中表示**"多种可能之一"**的强大工具,类似于 Solidity 中的枚举但功能更强大。

1. 定义枚举

rust 复制代码
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

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

fn main() {
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));
}

2. Option<T> 枚举

Rust 没有 null,而是使用 Option<T> 表示"有值"或"无值"。

rust 复制代码
enum Option<T> {
    Some(T),
    None,
}

fn main() {
    let some_number = Some(5);
    let some_string = Some("hello");
    let absent_number: Option<i32> = None; // 需要显式类型
}

使用 Option 时,必须处理 None 情况。

3. match 表达式

match 类似于 switch,但更强大,可以匹配枚举、字面量、范围等。

rust 复制代码
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

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

_ 通配符:匹配所有情况。

rust 复制代码
let dice_roll = 9;
match dice_roll {
    3 => println!("得到 3"),
    7 => println!("得到 7"),
    _ => println!("其他值"),
}

4. if let 简洁匹配

当只关心一种模式时,if let 更简洁。

rust 复制代码
let coin = Coin::Quarter;
if let Coin::Quarter = coin {
    println!("这是 25 美分");
}

三、错误处理

Rust 将错误分为两类:可恢复错误 (使用 Result<T, E>)和不可恢复错误 (使用 panic!)。

1. panic!

当程序遇到不可恢复错误时,可以调用 panic! 导致程序崩溃。

rust 复制代码
fn main() {
    panic!("发生致命错误!");
}

2. Result<T, E> 枚举

rust 复制代码
enum Result<T, E> {
    Ok(T),
    Err(E),
}

常见用法:

rust 复制代码
use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let file = File::open("hello.txt");

    let file = match file {
        Ok(f) => f,
        Err(e) => match e.kind() {
            ErrorKind::NotFound => panic!("文件不存在"),
            other => panic!("其他错误: {:?}", other),
        },
    };
}

3. 快捷方法:unwrapexpect

  • unwrap():如果 ResultOk,返回值;如果是 Err,调用 panic!
  • expect(msg):类似,但可自定义 panic 消息。
rust 复制代码
let f = File::open("hello.txt").unwrap();
let f = File::open("hello.txt").expect("无法打开文件");

4. 传播错误:? 运算符

? 用于将错误向上传播,使代码更简洁。

rust 复制代码
use std::fs::File;
use std::io;
use std::io::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)
}

? 会自动将错误类型转换(如果实现了 From trait)。

在 Solana 程序中 :通常使用 ProgramResultResult<(), ProgramError>),并在链上函数中返回 Result


四、泛型 (Generics)

泛型允许编写可处理多种类型的代码,类似于 Solidity 中的库函数或 C++ 模板。

1. 函数中的泛型

rust 复制代码
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];
    for &item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    let numbers = vec![34, 50, 25, 100, 65];
    let result = largest(&numbers);
    println!("最大值: {}", result);
}

T: PartialOrd + CopyTrait Bound ,要求 T 实现了比较和复制。

2. 结构体中的泛型

rust 复制代码
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

3. 枚举中的泛型

Option<T>Result<T, E> 就是泛型枚举。


五、Trait

Trait 类似于 Solidity 中的接口,定义共享行为。

1. 定义与实现 Trait

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, {}", self.headline, self.content)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

2. 默认实现

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(阅读更多...)")
    }
}

3. Trait 作为参数

rust 复制代码
pub fn notify(item: &impl Summary) {
    println!("新闻摘要: {}", item.summarize());
}

4. 常用 Trait

Trait 用途
Debug 打印调试信息(用 {:?}
Clone 显式复制
Copy 隐式复制(仅适用于简单类型)
PartialEq 相等比较(==, !=
PartialOrd 比较大小
Default 默认值

六、集合类型

1. Vec<T>(动态数组)

rust 复制代码
fn main() {
    let mut v: Vec<i32> = Vec::new();
    v.push(1);
    v.push(2);

    let v2 = vec![1, 2, 3];

    // 读取元素
    let third = &v2[2]; // 索引访问(可能 panic)
    let third_opt = v2.get(2); // 返回 Option<&i32>

    // 遍历
    for i in &v2 {
        println!("{}", i);
    }

    // 遍历并修改
    for i in &mut v {
        *i += 10;
    }
}

2. HashMap<K, V>(哈希映射)

类似于 Solidity 的 mapping,但更灵活。

rust 复制代码
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert("蓝队".to_string(), 10);
    scores.insert("黄队".to_string(), 50);

    // 获取值
    let team_name = "蓝队".to_string();
    let score = scores.get(&team_name).copied().unwrap_or(0);

    // 遍历
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }

    // 插入或更新
    scores.insert("黄队".to_string(), 60); // 覆盖旧值
    scores.entry("红队".to_string()).or_insert(30); // 不存在才插入
}

3. HashSet<T>

不重复元素的集合。

rust 复制代码
use std::collections::HashSet;

let mut set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(1); // 无变化

七、综合示例:代币账户管理(模拟 Solana)

我们结合所学内容,模拟一个简单代币账户的管理器,包含创建账户、转账和查询余额。

rust 复制代码
use std::collections::HashMap;

#[derive(Debug, Clone)]
struct TokenAccount {
    owner: String,
    balance: u64,
}

#[derive(Debug)]
enum AccountError {
    AccountNotFound,
    InsufficientBalance,
    OwnerMismatch,
}

type Result<T> = std::result::Result<T, AccountError>;

struct TokenManager {
    accounts: HashMap<String, TokenAccount>,
}

impl TokenManager {
    fn new() -> Self {
        TokenManager {
            accounts: HashMap::new(),
        }
    }

    // 创建账户
    fn create_account(&mut self, address: String, owner: String) {
        let account = TokenAccount { owner, balance: 0 };
        self.accounts.insert(address, account);
    }

    // 查询余额
    fn get_balance(&self, address: &str) -> Result<u64> {
        let account = self.accounts.get(address).ok_or(AccountError::AccountNotFound)?;
        Ok(account.balance)
    }

    // 转账
    fn transfer(&mut self, from: &str, to: &str, amount: u64) -> Result<()> {
        // 获取并检查 from 账户
        let from_account = self.accounts.get_mut(from).ok_or(AccountError::AccountNotFound)?;
        if from_account.balance < amount {
            return Err(AccountError::InsufficientBalance);
        }
        from_account.balance -= amount;

        // 获取并更新 to 账户
        let to_account = self.accounts.get_mut(to).ok_or(AccountError::AccountNotFound)?;
        to_account.balance += amount;

        Ok(())
    }

    // 存款(增加余额)
    fn deposit(&mut self, address: &str, amount: u64) -> Result<()> {
        let account = self.accounts.get_mut(address).ok_or(AccountError::AccountNotFound)?;
        account.balance += amount;
        Ok(())
    }
}

fn main() -> Result<()> {
    let mut manager = TokenManager::new();
    manager.create_account("alice".to_string(), "Alice".to_string());
    manager.create_account("bob".to_string(), "Bob".to_string());

    manager.deposit("alice", 100)?;
    manager.transfer("alice", "bob", 30)?;

    let alice_balance = manager.get_balance("alice")?;
    let bob_balance = manager.get_balance("bob")?;

    println!("Alice 余额: {}", alice_balance);
    println!("Bob 余额: {}", bob_balance);

    Ok(())
}

关键点

  • 使用 Result? 进行错误传播。
  • 结构体和方法封装状态。
  • HashMap 作为账户存储。
  • 泛型未直接使用,但 Result<T, E> 是泛型。

八、总结与下一步

本系列第三篇涵盖了 Rust 编程的核心主题:

  • 结构体与方法:自定义数据类型,封装行为。
  • 枚举与模式匹配OptionResultmatchif let
  • 错误处理panic!Result? 运算符。
  • 泛型与 Trait:编写灵活、可重用的代码。
  • 集合类型VecHashMapHashSet
  • 综合示例:代币账户管理。

现在,你已经具备了编写实用 Rust 程序的能力。在下一篇中,我们将进入 Solana 程序的实战开发,使用 Anchor 框架编写和部署链上程序。

延伸阅读

如果你对某个话题有疑问或想深入,欢迎在评论区讨论!

相关推荐
doiito2 小时前
【Agent Harness】Gliding Horse 上下文感知与智能压缩:让 Agent 的“注意力”永不偏移
ai·rust·架构设计·系统设计·ai agent
花褪残红青杏小1 天前
Rust图像处理第9节-Sobel 边缘检测:第一个真正用卷积的算法
rust·webassembly·图形学
doiito1 天前
【Agent Harness】Gliding Horse L2 作战地图深度优化:给多 Agent 上下文装上“精准导航”
ai·rust·架构设计·系统设计·ai agent
花褪残红青杏小1 天前
Rust图像处理第8节-暗角 & 复古胶片特效:四周衰减中心高亮
rust·webassembly·图形学
独孤留白2 天前
从C到Rust:Rust 的 Trait 不是Interface,那是什么?
rust
花褪残红青杏小2 天前
Rust图像处理第7节-马赛克像素化:分块取平均色实现打码风格
rust·webassembly·图形学
doiito3 天前
【Agent Harness】Gliding Horse 设计细节 -- 不跟风开发自己的AI Agent
架构·rust·agent
doiito3 天前
【Agent Harness】Gliding Horse 核心设计理念,不跟风开发自己的AI Agent
ai·rust·架构设计·系统设计·ai agent