返回 SC 笔记
SC Day 16

Rust - Vec + HashMap + HashSet + 迭代器(iter/map/filter/collect)

### 1. Vec\<T\:动态数组

2026-04-16
第一阶段:基础构建
rustcollectionsiteratorvechashmapfunctional-programming

日期: 2026-04-16 方向: Rust 阶段: 第一阶段:基础构建 标签: #rust #collections #iterator #vec #hashmap #functional-programming


今日目标

类型内容
学习掌握 Rust 核心集合类型和迭代器链式编程范式
实操用 HashMap 实现地址-余额映射,用迭代器链处理交易数据
产出完整的集合操作代码 + 迭代器链式数据处理管线

核心概念

1. Vec<T>:动态数组

Vec<T> 是 Rust 中最常用的集合类型,类似于其他语言的 ArrayList/Array。在区块链开发中,用于存储交易列表、区块数据、日志等。

fn vec_basics() {
    // ====== 创建 ======
    let mut txs: Vec<String> = Vec::new();
    let blocks = vec![1, 2, 3, 4, 5]; // vec! 宏快速创建
    let zeros = vec![0u8; 32]; // 32个0(用于初始化哈希)

    // ====== 添加元素 ======
    txs.push("0xabc".to_string());
    txs.push("0xdef".to_string());
    txs.push("0x123".to_string());

    // ====== 访问元素 ======
    // 方法1:索引访问(越界会 panic)
    let first = &txs[0]; // "0xabc"

    // 方法2:get 返回 Option(安全)
    let maybe_tx = txs.get(10); // None,不会 panic
    if let Some(tx) = txs.get(0) {
        println!("第一笔交易: {}", tx);
    }

    // ====== 修改元素 ======
    txs[0] = "0xnew".to_string();

    // ====== 删除元素 ======
    let last = txs.pop(); // 移除并返回最后一个元素,O(1)
    txs.remove(0); // 移除指定索引,O(n)(后续元素前移)

    // ====== 长度和容量 ======
    println!("长度: {}, 容量: {}", txs.len(), txs.capacity());
    txs.shrink_to_fit(); // 释放多余内存

    // ====== 搜索 ======
    let has_abc = txs.contains(&"0xabc".to_string());
    let position = txs.iter().position(|tx| tx.starts_with("0xd"));

    // ====== 排序 ======
    let mut amounts = vec![100u64, 5, 42, 1000, 7];
    amounts.sort(); // 升序
    amounts.sort_by(|a, b| b.cmp(a)); // 降序

    // ====== 切片 ======
    let first_three = &amounts[0..3]; // 借用前3个元素

    // ====== 去重 ======
    let mut addrs = vec!["0xa", "0xb", "0xa", "0xc", "0xb"];
    addrs.sort();
    addrs.dedup(); // 去除相邻重复(需先排序)
    // addrs = ["0xa", "0xb", "0xc"]
}

2. HashMap<K, V>:键值对映射

HashMap 是 Rust 中的哈希表,类似 Solidity 的 mapping。在区块链场景中,最常见的就是地址到余额的映射。

use std::collections::HashMap;

/// 模拟 ERC20 余额管理
struct TokenBalances {
    balances: HashMap<String, u128>,
    total_supply: u128,
}

impl TokenBalances {
    fn new() -> Self {
        TokenBalances {
            balances: HashMap::new(),
            total_supply: 0,
        }
    }

    /// 铸造代币
    fn mint(&mut self, to: &str, amount: u128) {
        // entry API:键存在则修改,不存在则插入默认值
        *self.balances.entry(to.to_string()).or_insert(0) += amount;
        self.total_supply += amount;
    }

    /// 查询余额
    fn balance_of(&self, address: &str) -> u128 {
        // 不存在返回 0(类似 Solidity mapping 默认值)
        *self.balances.get(address).unwrap_or(&0)
    }

    /// 转账
    fn transfer(&mut self, from: &str, to: &str, amount: u128) -> Result<(), String> {
        let from_balance = self.balance_of(from);
        if from_balance < amount {
            return Err(format!(
                "余额不足: 需要 {}, 可用 {}",
                amount, from_balance
            ));
        }

        // 扣减发送方
        *self.balances.get_mut(from).unwrap() -= amount;
        // 增加接收方
        *self.balances.entry(to.to_string()).or_insert(0) += amount;

        Ok(())
    }

    /// 获取所有持有者
    fn holders(&self) -> Vec<&str> {
        self.balances.iter()
            .filter(|(_, &balance)| balance > 0)
            .map(|(addr, _)| addr.as_str())
            .collect()
    }

    /// 获取前 N 大持有者
    fn top_holders(&self, n: usize) -> Vec<(&str, u128)> {
        let mut sorted: Vec<_> = self.balances.iter()
            .filter(|(_, &b)| b > 0)
            .map(|(addr, &bal)| (addr.as_str(), bal))
            .collect();

        sorted.sort_by(|a, b| b.1.cmp(&a.1)); // 按余额降序
        sorted.truncate(n);
        sorted
    }
}

fn hashmap_demo() {
    let mut token = TokenBalances::new();

    // 初始铸造
    token.mint("0xAlice", 1_000_000);
    token.mint("0xBob", 500_000);
    token.mint("0xCharlie", 250_000);

    // 转账
    token.transfer("0xAlice", "0xDave", 100_000).unwrap();

    // 查询
    println!("Alice 余额: {}", token.balance_of("0xAlice")); // 900_000
    println!("总供应量: {}", token.total_supply); // 1_750_000
    println!("持有者: {:?}", token.holders());
    println!("Top 2: {:?}", token.top_holders(2));

    // HashMap 其他常用操作
    let mut gas_prices: HashMap<&str, u64> = HashMap::new();

    // insert:插入键值对,返回旧值
    gas_prices.insert("slow", 10);
    gas_prices.insert("normal", 25);
    gas_prices.insert("fast", 50);

    // 检查键是否存在
    if gas_prices.contains_key("fast") {
        println!("快速 Gas: {}", gas_prices["fast"]);
    }

    // 遍历
    for (speed, price) in &gas_prices {
        println!("{}: {} gwei", speed, price);
    }

    // 只遍历值
    let avg: u64 = gas_prices.values().sum::<u64>() / gas_prices.len() as u64;
    println!("平均 Gas: {} gwei", avg);

    // 移除
    gas_prices.remove("slow");

    // entry API 高级用法
    // or_insert_with:只在键不存在时执行闭包
    gas_prices.entry("priority")
        .or_insert_with(|| {
            println!("计算 priority gas...");
            100 // 这个闭包只在键不存在时执行
        });
}

3. HashSet<T>:无序唯一集合

use std::collections::HashSet;

fn hashset_demo() {
    // 用于去重地址、检查交互过的协议等
    let mut interacted_protocols: HashSet<String> = HashSet::new();

    interacted_protocols.insert("Uniswap".to_string());
    interacted_protocols.insert("Aave".to_string());
    interacted_protocols.insert("Compound".to_string());
    interacted_protocols.insert("Uniswap".to_string()); // 重复,不会插入

    println!("交互协议数: {}", interacted_protocols.len()); // 3
    println!("用过 Uniswap: {}", interacted_protocols.contains("Uniswap")); // true

    // 集合运算
    let alice_protocols: HashSet<&str> = ["Uniswap", "Aave", "Curve"].iter().copied().collect();
    let bob_protocols: HashSet<&str> = ["Uniswap", "Compound", "Maker"].iter().copied().collect();

    // 交集:两人都用过的
    let both: HashSet<_> = alice_protocols.intersection(&bob_protocols).collect();
    println!("都用过: {:?}", both); // {"Uniswap"}

    // 并集:至少一人用过的
    let either: HashSet<_> = alice_protocols.union(&bob_protocols).collect();
    println!("至少一人用过: {:?}", either);

    // 差集:Alice 用过但 Bob 没用的
    let alice_only: HashSet<_> = alice_protocols.difference(&bob_protocols).collect();
    println!("Alice 独有: {:?}", alice_only); // {"Aave", "Curve"}

    // 对称差集:只被一个人用过的
    let unique: HashSet<_> = alice_protocols.symmetric_difference(&bob_protocols).collect();
    println!("各自独有: {:?}", unique);

    // 判断关系
    let defi_protocols: HashSet<&str> =
        ["Uniswap", "Aave", "Compound", "Curve", "Maker", "dYdX"].iter().copied().collect();
    println!("Alice 是 DeFi 子集: {}", alice_protocols.is_subset(&defi_protocols)); // true
}

4. 迭代器 (Iterator):函数式数据处理

迭代器是 Rust 中处理数据序列的核心抽象。通过链式调用 mapfilterfold 等适配器,可以构建高效的数据处理管线。

迭代器基础

fn iterator_basics() {
    let numbers = vec![1, 2, 3, 4, 5];

    // iter():借用元素 (&T)
    for n in numbers.iter() {
        println!("{}", n); // n 是 &i32
    }

    // iter_mut():可变借用元素 (&mut T)
    let mut numbers = vec![1, 2, 3, 4, 5];
    for n in numbers.iter_mut() {
        *n *= 2; // 修改每个元素
    }

    // into_iter():消费集合,获得所有权 (T)
    let numbers = vec![1, 2, 3, 4, 5];
    for n in numbers.into_iter() {
        println!("{}", n); // n 是 i32
    }
    // numbers 在这里已经被消费,不能再使用
}

核心迭代器适配器

#[derive(Debug, Clone)]
struct Transaction {
    hash: String,
    from: String,
    to: String,
    value_eth: f64,
    gas_used: u64,
    success: bool,
    block_number: u64,
}

fn iterator_adapters(transactions: &[Transaction]) {
    // ====== map:转换每个元素 ======
    let hashes: Vec<&str> = transactions.iter()
        .map(|tx| tx.hash.as_str())
        .collect();

    // ====== filter:筛选元素 ======
    let large_txs: Vec<&Transaction> = transactions.iter()
        .filter(|tx| tx.value_eth > 10.0)
        .collect();

    // ====== filter_map:筛选 + 转换合并 ======
    let successful_values: Vec<f64> = transactions.iter()
        .filter_map(|tx| {
            if tx.success { Some(tx.value_eth) } else { None }
        })
        .collect();

    // ====== fold:累积计算 ======
    let total_value: f64 = transactions.iter()
        .filter(|tx| tx.success)
        .fold(0.0, |acc, tx| acc + tx.value_eth);

    // ====== sum:数值求和(fold 的简化版)======
    let total_gas: u64 = transactions.iter()
        .map(|tx| tx.gas_used)
        .sum();

    // ====== enumerate:带索引遍历 ======
    for (i, tx) in transactions.iter().enumerate() {
        println!("#{}: {} ({} ETH)", i, tx.hash, tx.value_eth);
    }

    // ====== zip:并行遍历两个迭代器 ======
    let names = vec!["Alice", "Bob", "Charlie"];
    let amounts = vec![1.0, 2.5, 0.1];
    let transfers: Vec<_> = names.iter()
        .zip(amounts.iter())
        .map(|(name, amount)| format!("{} 转了 {} ETH", name, amount))
        .collect();

    // ====== take / skip:截取 ======
    let first_five: Vec<_> = transactions.iter().take(5).collect();
    let after_ten: Vec<_> = transactions.iter().skip(10).collect();

    // ====== chain:连接两个迭代器 ======
    let block1_txs = vec!["0xa", "0xb"];
    let block2_txs = vec!["0xc", "0xd"];
    let all_txs: Vec<_> = block1_txs.iter().chain(block2_txs.iter()).collect();

    // ====== any / all:布尔判断 ======
    let has_failed = transactions.iter().any(|tx| !tx.success);
    let all_small = transactions.iter().all(|tx| tx.value_eth < 100.0);

    // ====== find:查找第一个匹配 ======
    let first_whale_tx = transactions.iter()
        .find(|tx| tx.value_eth > 1000.0);

    // ====== count / min / max ======
    let failed_count = transactions.iter()
        .filter(|tx| !tx.success)
        .count();

    let max_value = transactions.iter()
        .map(|tx| tx.value_eth)
        .fold(f64::NEG_INFINITY, f64::max);

    // ====== flat_map:扁平化映射 ======
    let blocks: Vec<Vec<String>> = vec![
        vec!["0xa".into(), "0xb".into()],
        vec!["0xc".into()],
        vec!["0xd".into(), "0xe".into(), "0xf".into()],
    ];
    let all_tx_hashes: Vec<&str> = blocks.iter()
        .flat_map(|block| block.iter().map(|s| s.as_str()))
        .collect();
    // ["0xa", "0xb", "0xc", "0xd", "0xe", "0xf"]

    // ====== partition:按条件分组 ======
    let (successful, failed): (Vec<_>, Vec<_>) = transactions.iter()
        .partition(|tx| tx.success);

    // ====== windows / chunks:滑动窗口 ======
    let prices = vec![100.0, 102.0, 99.0, 105.0, 103.0];
    let changes: Vec<f64> = prices.windows(2)
        .map(|w| w[1] - w[0])
        .collect();
    // [2.0, -3.0, 6.0, -2.0]
}

代码实战:区块链交易分析管线

use std::collections::{HashMap, HashSet};

#[derive(Debug, Clone)]
struct TxRecord {
    hash: String,
    from: String,
    to: String,
    value_wei: u128,
    gas_price_gwei: u64,
    gas_used: u64,
    success: bool,
    block_number: u64,
    timestamp: u64,
}

impl TxRecord {
    fn value_eth(&self) -> f64 {
        self.value_wei as f64 / 1e18
    }

    fn gas_cost_eth(&self) -> f64 {
        (self.gas_price_gwei as f64 * self.gas_used as f64) / 1e9
    }
}

/// 交易分析器
struct TxAnalyzer {
    transactions: Vec<TxRecord>,
}

impl TxAnalyzer {
    fn new(transactions: Vec<TxRecord>) -> Self {
        TxAnalyzer { transactions }
    }

    /// 统计基本指标
    fn basic_stats(&self) -> BasicStats {
        let total_count = self.transactions.len();
        let success_count = self.transactions.iter()
            .filter(|tx| tx.success)
            .count();
        let total_value: f64 = self.transactions.iter()
            .filter(|tx| tx.success)
            .map(|tx| tx.value_eth())
            .sum();
        let total_gas_cost: f64 = self.transactions.iter()
            .map(|tx| tx.gas_cost_eth())
            .sum();
        let avg_gas_price: f64 = self.transactions.iter()
            .map(|tx| tx.gas_price_gwei as f64)
            .sum::<f64>() / total_count as f64;

        BasicStats {
            total_count,
            success_count,
            fail_count: total_count - success_count,
            success_rate: success_count as f64 / total_count as f64 * 100.0,
            total_value_eth: total_value,
            total_gas_cost_eth: total_gas_cost,
            avg_gas_price_gwei: avg_gas_price,
        }
    }

    /// 按发送者分组统计
    fn stats_by_sender(&self) -> HashMap<String, SenderStats> {
        let mut stats: HashMap<String, SenderStats> = HashMap::new();

        for tx in &self.transactions {
            let entry = stats.entry(tx.from.clone()).or_insert(SenderStats {
                address: tx.from.clone(),
                tx_count: 0,
                total_sent_eth: 0.0,
                total_gas_eth: 0.0,
                unique_recipients: HashSet::new(),
            });

            entry.tx_count += 1;
            if tx.success {
                entry.total_sent_eth += tx.value_eth();
            }
            entry.total_gas_eth += tx.gas_cost_eth();
            entry.unique_recipients.insert(tx.to.clone());
        }

        stats
    }

    /// 识别鲸鱼交易(> threshold ETH)
    fn whale_transactions(&self, threshold_eth: f64) -> Vec<&TxRecord> {
        self.transactions.iter()
            .filter(|tx| tx.success && tx.value_eth() > threshold_eth)
            .collect()
    }

    /// 找出最活跃的地址 (Top N)
    fn most_active_addresses(&self, n: usize) -> Vec<(String, usize)> {
        let mut address_counts: HashMap<String, usize> = HashMap::new();

        for tx in &self.transactions {
            *address_counts.entry(tx.from.clone()).or_insert(0) += 1;
            *address_counts.entry(tx.to.clone()).or_insert(0) += 1;
        }

        let mut sorted: Vec<_> = address_counts.into_iter().collect();
        sorted.sort_by(|a, b| b.1.cmp(&a.1));
        sorted.truncate(n);
        sorted
    }

    /// Gas 价格分布
    fn gas_price_distribution(&self) -> HashMap<String, usize> {
        self.transactions.iter()
            .map(|tx| {
                match tx.gas_price_gwei {
                    0..=10 => "低 (0-10 gwei)".to_string(),
                    11..=30 => "正常 (11-30 gwei)".to_string(),
                    31..=100 => "高 (31-100 gwei)".to_string(),
                    _ => "极高 (>100 gwei)".to_string(),
                }
            })
            .fold(HashMap::new(), |mut acc, tier| {
                *acc.entry(tier).or_insert(0) += 1;
                acc
            })
    }

    /// 按区块聚合
    fn aggregate_by_block(&self) -> Vec<BlockSummary> {
        let mut block_map: HashMap<u64, Vec<&TxRecord>> = HashMap::new();

        for tx in &self.transactions {
            block_map.entry(tx.block_number).or_default().push(tx);
        }

        let mut summaries: Vec<BlockSummary> = block_map.into_iter()
            .map(|(block_num, txs)| {
                BlockSummary {
                    block_number: block_num,
                    tx_count: txs.len(),
                    total_value_eth: txs.iter()
                        .filter(|tx| tx.success)
                        .map(|tx| tx.value_eth())
                        .sum(),
                    total_gas_used: txs.iter()
                        .map(|tx| tx.gas_used)
                        .sum(),
                    unique_senders: txs.iter()
                        .map(|tx| tx.from.as_str())
                        .collect::<HashSet<_>>()
                        .len(),
                }
            })
            .collect();

        summaries.sort_by_key(|s| s.block_number);
        summaries
    }
}

#[derive(Debug)]
struct BasicStats {
    total_count: usize,
    success_count: usize,
    fail_count: usize,
    success_rate: f64,
    total_value_eth: f64,
    total_gas_cost_eth: f64,
    avg_gas_price_gwei: f64,
}

struct SenderStats {
    address: String,
    tx_count: usize,
    total_sent_eth: f64,
    total_gas_eth: f64,
    unique_recipients: HashSet<String>,
}

#[derive(Debug)]
struct BlockSummary {
    block_number: u64,
    tx_count: usize,
    total_value_eth: f64,
    total_gas_used: u64,
    unique_senders: usize,
}

fn main() {
    // 创建模拟数据
    let transactions = vec![
        TxRecord {
            hash: "0x001".into(), from: "0xAlice".into(), to: "0xBob".into(),
            value_wei: 1_000_000_000_000_000_000, // 1 ETH
            gas_price_gwei: 20, gas_used: 21000,
            success: true, block_number: 100, timestamp: 1700000000,
        },
        TxRecord {
            hash: "0x002".into(), from: "0xWhale".into(), to: "0xDEX".into(),
            value_wei: 500_000_000_000_000_000_000, // 500 ETH
            gas_price_gwei: 50, gas_used: 150000,
            success: true, block_number: 100, timestamp: 1700000012,
        },
        TxRecord {
            hash: "0x003".into(), from: "0xBob".into(), to: "0xCharlie".into(),
            value_wei: 100_000_000_000_000_000, // 0.1 ETH
            gas_price_gwei: 15, gas_used: 21000,
            success: false, block_number: 101, timestamp: 1700000024,
        },
    ];

    let analyzer = TxAnalyzer::new(transactions);

    // 基本统计
    let stats = analyzer.basic_stats();
    println!("=== 基本统计 ===");
    println!("总交易: {}, 成功率: {:.1}%", stats.total_count, stats.success_rate);
    println!("总价值: {:.2} ETH", stats.total_value_eth);
    println!("总 Gas 消耗: {:.6} ETH", stats.total_gas_cost_eth);

    // 鲸鱼交易
    let whale_txs = analyzer.whale_transactions(100.0);
    println!("\n=== 鲸鱼交易 (>100 ETH) ===");
    for tx in whale_txs {
        println!("{}: {:.2} ETH ({} -> {})", tx.hash, tx.value_eth(), tx.from, tx.to);
    }

    // 最活跃地址
    let active = analyzer.most_active_addresses(3);
    println!("\n=== 最活跃地址 ===");
    for (addr, count) in active {
        println!("{}: {} 笔交易", addr, count);
    }

    // 按区块聚合
    let blocks = analyzer.aggregate_by_block();
    println!("\n=== 区块摘要 ===");
    for block in blocks {
        println!(
            "Block #{}: {} 笔, {:.2} ETH, {} 个发送者",
            block.block_number, block.tx_count,
            block.total_value_eth, block.unique_senders
        );
    }
}

关键要点总结

要点说明
Vec 是堆分配的动态数组push/pop O(1),insert/remove O(n)
HashMap 的 entry APIentry().or_insert() 优雅处理"存在则更新,不存在则插入"
HashSet 用于去重和集合运算intersection/union/difference
iter() 借用,into_iter() 消费需要保留原集合用 iter(),不需要用 into_iter()
迭代器是惰性的只有 collect/sum/fold 等消费者才会触发计算
迭代器链零开销编译器会将链式调用优化为单次循环
collect 需要类型注解编译器需要知道收集成什么类型

常见误区

误区 1:在迭代时修改集合

let mut v = vec![1, 2, 3, 4, 5];
// 编译错误!不能在迭代 v 的同时修改 v
// for x in &v {
//     if *x > 3 { v.push(*x * 2); }
// }

// 正确做法:先收集,再修改
let to_add: Vec<_> = v.iter().filter(|&&x| x > 3).map(|&x| x * 2).collect();
v.extend(to_add);

// 或者用 retain 删除元素
v.retain(|&x| x <= 3); // 保留 <= 3 的元素

误区 2:混淆 iter() 和 into_iter()

let names = vec!["Alice".to_string(), "Bob".to_string()];

// iter() 借用,names 之后仍可用
let refs: Vec<&String> = names.iter().collect();
println!("{:?}", names); // OK

// into_iter() 消费,names 之后不可用
let owned: Vec<String> = names.into_iter().collect();
// println!("{:?}", names); // 编译错误!names 已被消费

误区 3:忘记迭代器是惰性的

let v = vec![1, 2, 3];
// 这一行什么都不做!迭代器只是声明了一个管线
v.iter().map(|x| {
    println!("{}", x); // 永远不会执行
    x * 2
});

// 必须有消费者触发执行
let doubled: Vec<_> = v.iter().map(|x| x * 2).collect(); // 现在执行了

面试关联

Q: Rust 的迭代器和手写 for 循环相比,性能如何?

30 秒回答:完全相同。Rust 编译器会将迭代器链式调用进行零开销抽象 (zero-cost abstraction) 优化,展开为等价的手写循环代码。实际上,在某些情况下迭代器版本甚至更快,因为编译器有更多优化信息(如 bounds check 消除)。Rust 官方文档明确声明迭代器是零开销的。

Q: HashMap 和 Solidity mapping 有什么区别?

特性Rust HashMapSolidity mapping
存储位置内存(堆)链上存储 (storage)
遍历支持不支持(无法遍历)
默认值无(返回 Option)有(所有类型零值)
删除remove() 真正删除delete 重置为零值
Gas 消耗读 ~2100 gas,写 ~20000 gas

参考资源

资源说明
The Rust Book - Ch8: Collections官方集合教程
The Rust Book - Ch13: Iterators迭代器详解
Iterator Cheat Sheet迭代器方法速查
Rust std::collections标准库集合文档
Rust Performance Book性能优化指南