Week 1 总结与复习
回顾 Week 1 所有核心概念,构建系统化知识框架
日期: 2026-04-16 方向: Solidity + Rust 阶段: 第一阶段:基础构建 标签: #review #comparison #week1 #solidity #rust
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | 回顾 Week 1 所有核心概念,构建系统化知识框架 |
| 实操 | 完成自测题,对比 Solidity 与 Rust 的关键差异 |
| 产出 | Week 1 知识图谱 + 对比表 + 自测通过 |
一、Week 1 学习路径回顾
Day 1: Solidity - Remix IDE + 数据类型 + 变量
Day 2: Rust - 安装环境 + Cargo + 变量/可变性 + 基本类型
Day 3: Solidity - 函数可见性 + 事件 + 错误处理
Day 4: Rust - 所有权 + 借用 + 引用
Day 5: Solidity - mapping + struct + array + enum
Day 6: Rust - 字符串 + 切片 + 生命周期
Day 7: 复习 - 总结对比 + 自测
二、Solidity vs Rust 全面对比
2.1 语言定位
| 维度 | Solidity | Rust |
|---|---|---|
| 设计目标 | EVM 智能合约 | 系统级编程 |
| 运行环境 | 以太坊虚拟机 (EVM) | 原生编译(x86/ARM) |
| 区块链用途 | Ethereum/L2 合约 | Solana/Near/Substrate 链上程序 |
| 内存模型 | storage/memory/calldata | 栈/堆/所有权系统 |
| 安全保障 | 运行时检查 + 审计 | 编译时保证 + 所有权 |
| 学习曲线 | 中等(像 JS/Java) | 陡峭(所有权/生命周期) |
| 包管理 | npm (Hardhat/Foundry) | Cargo(内置) |
| 测试 | 外部框架 (Foundry/Hardhat) | 内置 (cargo test) |
| 编译速度 | 很快 | 较慢(但增量编译快) |
2.2 数据类型对比
| 类型 | Solidity | Rust | 说明 |
|---|---|---|---|
| 整数 | uint256 (默认) | i32 (默认) | Solidity 偏向大数 |
| 无符号 | uint8 ~ uint256 | u8 ~ u128 | Solidity 最大 256位 |
| 有符号 | int8 ~ int256 | i8 ~ i128 | - |
| 布尔 | bool | bool | 相同 |
| 地址 | address (20 bytes) | 无原生类型 | Rust 用 [u8; 32] |
| 字符串 | string | String / &str | Rust 区分所有权 |
| 固定字节 | bytes1 ~ bytes32 | [u8; N] | 类似 |
| 动态字节 | bytes | Vec<u8> | 类似 |
| 浮点 | 无! | f32 / f64 | 区块链避免浮点 |
| 数组 | T[] / T[N] | Vec<T> / [T; N] | 类似 |
| 映射 | mapping(K => V) | HashMap<K, V> | Solidity 不可遍历 |
| 枚举 | enum (简单) | enum (带数据) | Rust 枚举更强大 |
| 结构体 | struct | struct | 类似 |
| 元组 | 函数返回值 | (T1, T2, ...) | Rust 更常用 |
2.3 变量与可变性对比
| 特性 | Solidity | Rust |
|---|---|---|
| 默认可变性 | 可变 | 不可变 |
| 声明可变变量 | 默认就是 | let mut x |
| 常量 | constant (编译时) | const (编译时) |
| 不可变量 | immutable (部署时) | let(默认行为) |
| 变量遮蔽 | 不支持 | 支持(Shadowing) |
| 类型推导 | 不支持(必须声明) | 支持(大部分情况) |
2.4 函数对比
| 特性 | Solidity | Rust |
|---|---|---|
| 可见性 | public/private/internal/external | pub / 默认私有 |
| 只读标记 | view | &self |
| 纯计算 | pure | 无特殊标记(pure 是默认) |
| 修饰符 | modifier | 无(用 trait 或手动检查) |
| 错误处理 | require/revert/assert/custom error | Result<T, E> / panic! |
| 返回值 | returns (type) | -> Type |
| 多返回值 | returns (T1, T2) | -> (T1, T2) 元组 |
| 重载 | 支持 | 不支持(但有 trait) |
| 默认参数 | 不支持 | 不支持 |
2.5 内存管理对比
| 概念 | Solidity | Rust |
|---|---|---|
| 持久存储 | storage(链上永久) | 无(需要序列化到磁盘) |
| 临时内存 | memory(函数调用内) | 栈(自动释放) |
| 只读数据 | calldata(交易输入) | &T(不可变引用) |
| 堆分配 | new T[](memory中) | Box<T>, Vec<T>, String |
| 垃圾回收 | EVM 自动管理 | 无需(所有权系统) |
| 成本 | gas(storage 最贵) | CPU 时间(堆分配较贵) |
2.6 事件/日志对比
| 特性 | Solidity | Rust |
|---|---|---|
| 日志机制 | event + emit | println! / log crate |
| 链上日志 | 交易日志(Logs) | Solana: msg! / emit! |
| 可过滤 | indexed 参数 | 取决于运行时环境 |
| 成本 | 很低(vs storage) | N/A(不涉及 gas) |
三、核心概念对照代码
3.1 "Hello World" 对比
// Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HelloWorld {
string public message = "Hello, Solidity!";
function setMessage(string calldata _msg) external {
message = _msg;
}
}
// Rust
fn main() {
let mut message = String::from("Hello, Rust!");
println!("{}", message);
message = String::from("New message");
println!("{}", message);
}
3.2 计数器对比
// Solidity
contract Counter {
uint256 public count;
function increment() external {
count += 1;
}
function decrement() external {
require(count > 0, "Count is zero");
count -= 1;
}
function getCount() external view returns (uint256) {
return count;
}
}
// Rust
struct Counter {
count: u64,
}
impl Counter {
fn new() -> Self {
Counter { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
fn decrement(&mut self) -> Result<(), String> {
if self.count == 0 {
return Err("Count is zero".to_string());
}
self.count -= 1;
Ok(())
}
fn get_count(&self) -> u64 {
self.count
}
}
fn main() {
let mut counter = Counter::new();
counter.increment();
counter.increment();
println!("Count: {}", counter.get_count()); // 2
match counter.decrement() {
Ok(()) => println!("Decremented: {}", counter.get_count()),
Err(e) => println!("Error: {}", e),
}
}
3.3 简单存储对比
// Solidity
contract SimpleStorage {
mapping(string => string) private store;
event ValueSet(address indexed setter, string key, string value);
error KeyEmpty();
function set(string calldata key, string calldata value) external {
if (bytes(key).length == 0) revert KeyEmpty();
store[key] = value;
emit ValueSet(msg.sender, key, value);
}
function get(string calldata key) external view returns (string memory) {
return store[key];
}
}
// Rust
use std::collections::HashMap;
struct SimpleStorage {
store: HashMap<String, String>,
}
impl SimpleStorage {
fn new() -> Self {
SimpleStorage {
store: HashMap::new(),
}
}
fn set(&mut self, key: &str, value: &str) -> Result<(), String> {
if key.is_empty() {
return Err("Key cannot be empty".to_string());
}
self.store.insert(key.to_string(), value.to_string());
println!("[Event] ValueSet: key={}, value={}", key, value);
Ok(())
}
fn get(&self, key: &str) -> Option<&String> {
self.store.get(key)
}
}
fn main() {
let mut storage = SimpleStorage::new();
storage.set("name", "Alice").unwrap();
storage.set("age", "25").unwrap();
match storage.get("name") {
Some(value) => println!("name = {}", value),
None => println!("Key not found"),
}
}
四、Week 1 关键要点清单
Solidity 要点
- 理解 Remix IDE 的编译和部署流程
- 掌握所有基础数据类型(uint/int/bool/address/bytes/string/enum)
- 理解状态变量、局部变量、全局变量的区别
- 理解 constant 和 immutable 的区别与 gas 优化
- 掌握四种函数可见性(public/private/internal/external)
- 理解 view 和 pure 函数
- 会写自定义 modifier
- 理解事件(event)的用途和 indexed 参数
- 掌握四种错误处理方式(require/revert/assert/custom error)
- 理解 mapping 的限制(不可遍历、不可检查存在性)
- 掌握 struct 的 storage vs memory 区别
- 理解动态数组的 gas 陷阱
Rust 要点
- 会用 Cargo 创建、构建、运行项目
- 理解 let(不可变)vs let mut(可变)vs const
- 理解 Shadowing 与 mut 的区别
- 掌握所有基本类型(i32/u64/f64/bool/char/tuple/array)
- 深入理解所有权三大规则
- 理解 Move 语义 vs Copy trait
- 掌握不可变引用(&T)和可变引用(&mut T)
- 牢记借用规则:多个 &T 或一个 &mut T
- 理解 String vs &str 的区别
- 会使用切片(&[T]、&str)
- 理解生命周期基础和省略规则
- 会解决常见的编译器错误(borrow of moved value 等)
五、自测题
Solidity 部分
Q1: 下面的代码有什么问题?
contract Bug {
uint8 public value = 300;
}
<details>
<summary>答案</summary>
uint8 最大值为 255,300 超出范围,编译时就会报错。应该使用 uint16 或更大的类型。
Q2: 以下代码的 getUser 函数有 bug,是什么?
contract UserStore {
struct User { string name; uint balance; }
mapping(address => User) users;
function updateBalance(address _addr, uint _amount) public {
User memory user = users[_addr];
user.balance = _amount;
}
}
<details>
<summary>答案</summary>
User memory user 是从 storage 复制到 memory 的副本。修改副本不会影响 storage 中的实际数据。应该改为 User storage user = users[_addr];
Q3: external 函数和 public 函数有什么区别?什么时候用 external?
external 只能从外部调用,参数可以使用 calldata(只读、不复制);public 内外都能调用,参数用 memory(复制到内存)。当函数只需要被外部调用且有大型数组/字符串参数时,用 external + calldata 更省 gas。
Q4: 以下四种错误处理方式,哪种 gas 消耗最低?
// A
require(x > 0, "Must be positive");
// B
if (x == 0) revert("Must be positive");
// C
assert(x > 0);
// D
error NotPositive();
if (x == 0) revert NotPositive();
<details>
<summary>答案</summary>
D 最省 gas。自定义错误只存储 4 字节的函数选择器,不需要存储和编码字符串。A 和 B 消耗类似(都有字符串),C 从 0.8.0 起也使用 revert,但没有错误信息。
</details>Q5: 为什么 mapping 不能遍历?如何实现遍历?
<details> <summary>答案</summary>Mapping 的键通过 keccak256 哈希后分散存储在 2^256 的存储空间中,没有维护键的列表,所以无法遍历。实现遍历需要额外维护一个数组存储所有键:mapping(address => uint) balances; address[] allUsers;
Rust 部分
Q6: 以下代码会编译通过吗?如果不会,为什么?
fn main() {
let s = String::from("hello");
let s2 = s;
println!("{}", s);
}
<details>
<summary>答案</summary>
不会。let s2 = s; 触发了 Move,s 的所有权转移给了 s2,之后 s 不再有效。println!("{}", s) 会报错 "borrow of moved value"。修复方法:使用 s.clone() 或传递引用 &s。
Q7: 以下代码有什么问题?
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s;
println!("{} {}", r1, r2);
}
<details>
<summary>答案</summary>
违反借用规则:不能同时存在不可变引用(r1)和可变引用(r2)。修复方法:在使用完 r1 之后再创建 r2。
Q8: String 和 &str 有什么区别?函数参数应该用哪个?
String 是堆分配的、拥有所有权的字符串;&str 是字符串切片(引用)。函数参数推荐用 &str,因为它更通用——既可以接受 &String(自动解引用),也可以接受字符串字面量。
Q9: 什么是 Shadowing?它和 mut 有什么区别?
Shadowing 是用同名 let 声明创建新变量,遮蔽旧变量。与 mut 的关键区别:(1) Shadowing 可以改变类型,mut 不行;(2) Shadowing 创建的是新变量,旧变量被遮蔽但不是被修改。
let x = "123"; // &str
let x = x.parse::<i32>().unwrap(); // i32, 类型改变!Shadowing OK
let mut y = "123";
// y = y.parse::<i32>().unwrap(); // ❌ 类型不能变
</details>
Q10: 生命周期标注 'a 是什么意思?什么时候需要?
'a 描述引用之间的有效范围关系。当函数接受多个引用参数并返回引用时,编译器需要知道返回的引用和哪个输入有关联。三条省略规则能自动推断大多数情况,需要显式标注的最常见场景是:两个引用输入,返回其中一个。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
</details>
六、Week 1 知识图谱
Week 1 知识图谱
│
├── Solidity 基础
│ ├── 环境: Remix IDE
│ ├── 数据类型
│ │ ├── 值类型: uint, int, bool, address, bytes, enum
│ │ ├── 引用类型: string, array, struct, mapping
│ │ └── 特殊: constant, immutable
│ ├── 变量
│ │ ├── 状态变量 (storage, 链上永久)
│ │ ├── 局部变量 (memory/stack, 临时)
│ │ └── 全局变量 (msg.sender, block.timestamp 等)
│ ├── 函数
│ │ ├── 可见性: public > external > internal > private
│ │ ├── 状态: 默认/view/pure
│ │ ├── modifier: 前置/后置检查
│ │ └── payable: 接收 ETH
│ ├── 事件: event + emit + indexed
│ ├── 错误处理: require/revert/assert/custom error
│ └── 数据结构
│ ├── mapping (KV, 不可遍历)
│ ├── struct (组合类型)
│ ├── array (固定/动态)
│ └── enum (状态机)
│
├── Rust 基础
│ ├── 环境: rustup + cargo
│ ├── 数据类型
│ │ ├── 标量: i32, u64, f64, bool, char
│ │ ├── 复合: tuple, array
│ │ └── 集合: Vec, String, HashMap
│ ├── 变量与可变性
│ │ ├── let (默认不可变)
│ │ ├── let mut (可变)
│ │ ├── const (编译时常量)
│ │ └── Shadowing (类型可变)
│ ├── ★★★ 所有权系统 ★★★
│ │ ├── 三大规则
│ │ ├── Move vs Copy
│ │ ├── clone (深拷贝)
│ │ └── Drop (自动释放)
│ ├── 借用与引用
│ │ ├── &T (不可变引用, 多个)
│ │ ├── &mut T (可变引用, 唯一)
│ │ └── 借用规则 (编译时检查)
│ └── 字符串与切片
│ ├── String vs &str
│ ├── 切片: &[T], &str
│ └── 生命周期 ('a)
│
└── 交叉对比
├── 内存模型: storage/memory/calldata vs 栈/堆/所有权
├── 可变性: Solidity 默认可变 vs Rust 默认不可变
├── 安全: 运行时检查 vs 编译时保证
└── 类型系统: Solidity 简单 vs Rust 强大(泛型/trait/enum)
七、下周预告
Week 2 计划:
Day 8: Solidity - ERC20 标准详解 + 手写完整实现
Day 9: Rust - struct + impl + 方法 + 关联函数
Day 10: Solidity - ERC20 进阶 + OpenZeppelin + mint/burn/pause
Day 11: Rust - enum + Option + Result + 模式匹配
Day 12: Solidity - 继承 + 接口 + 抽象合约
Day 13: Rust - trait + 泛型基础
Day 14: 复习 - Week 2 总结
Week 2 的核心目标:
- 完全理解 ERC20 代币标准——这是 DeFi 的基石
- 掌握 Rust 的 struct/enum/trait——这是 Rust 面向对象的核心
- 开始理解"接口"和"抽象"的概念——Solidity 的继承 vs Rust 的 trait
八、常见误区回顾
Week 1 最容易犯的 5 个错误
-
Solidity: 用 memory 修改 struct 以为会保存到链上 → 必须用 storage 引用
-
Rust: 到处 .clone() 让编译器不报错 → 先理解为什么报错,优先用引用
-
Solidity: 在循环中遍历长数组 → 可能超 gas limit,必须分页
-
Rust: 同时创建 &T 和 &mut T → 理解借用规则是唯一的出路
-
通用: 以为 private 数据真的不可读 → 链上数据全部公开,private 只是接口限制
九、面试关联
综合面试题
Q: 如果你要在 Solidity 和 Rust(Solana)之间选择构建一个 DeFi 协议,你会如何考虑?
<details> <summary>参考答案</summary>选择维度:
-
生态系统:Solidity 的 DeFi 生态更成熟(Uniswap/Aave/Compound 都是 Solidity),开发工具和审计资源更丰富。Solana/Rust 的 DeFi 生态较新但增长快。
-
性能需求:如果需要高吞吐量和低延迟(如订单簿 DEX),Solana+Rust 更适合。如果是标准 AMM/借贷,以太坊+Solidity 足够。
-
安全考虑:Solidity 的审计工具和审计师更多,但 Rust 的编译时安全性更强。Solana 程序的安全模型不同(账户模型 vs 合约存储),需要不同的安全思维。
-
用户基础:以太坊用户最多,TVL 最高。但 Solana 吸引了大量零售用户(低 gas、快速确认)。
-
开发效率:Solidity 学习曲线低,快速原型开发;Rust 学习曲线高,但代码质量更可靠。
-
组合性:以太坊的合约可组合性(composability)是 DeFi 的核心优势。Solana 的 CPI(Cross-Program Invocation)也支持,但模式不同。
我的建议:看目标用户。面向机构/高价值交易 → 以太坊/L2 + Solidity。面向零售/高频交易 → Solana + Rust。或者用 Solidity 在 L2 上部署,兼顾成本和生态。
</details>十、参考资源
本周所有参考资源汇总
| 类别 | 资源 | 链接 |
|---|---|---|
| Solidity 官方 | Solidity Docs | https://docs.soliditylang.org/ |
| Solidity 示例 | Solidity by Example | https://solidity-by-example.org/ |
| Solidity IDE | Remix | https://remix.ethereum.org/ |
| Rust 官方 | The Rust Book | https://doc.rust-lang.org/book/ |
| Rust 示例 | Rust by Example | https://doc.rust-lang.org/rust-by-example/ |
| Rust 练习 | Rustlings | https://github.com/rust-lang/rustlings |
| Rust 在线 | Rust Playground | https://play.rust-lang.org/ |
| 测试网 | Sepolia Etherscan | https://sepolia.etherscan.io/ |
| OpenZeppelin | 合约安全库 | https://docs.openzeppelin.com/ |
| Solana | Solana Cookbook | https://solanacookbook.com/ |