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 中处理数据序列的核心抽象。通过链式调用 map、filter、fold 等适配器,可以构建高效的数据处理管线。
迭代器基础
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 API | entry().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 HashMap | Solidity 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 | 性能优化指南 |