-
Notifications
You must be signed in to change notification settings - Fork 896
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
lizhe
committed
Nov 23, 2024
1 parent
cd1314e
commit 83d0f0a
Showing
2 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,323 @@ | ||
# (十一)Move泛型:从入门到“我真的会了” | ||
|
||
在完成HOH水分子社区Task的过程中,经常会用到泛型,学习Move语言的泛型功能,就像在编程的魔法学校里解锁了一个强大的咒语系统。这不仅可以帮助你轻松应对各种数据类型,还能让你的代码写得既简洁又灵活。但如果没有深刻理解,可能就会不小心变成“危险施法”,引发意想不到的类型错误。那么,今天我们就来深入探讨Move泛型的各种“招式”,从入门到“我真的会了”。 | ||
|
||
## 什么是泛型? | ||
|
||
泛型(Generics)是一种可以为函数和结构体定义通用类型的特性,这也被称为“参数化多态”。在Move中,我们通常用“类型参数”(Type Parameters)和“类型实参”(Type Arguments)来描述它。 | ||
|
||
简单来说,泛型是为了写出“一劳永逸”的代码。比如,当你需要一个可以处理任意数据类型的向量(vector)或一个可以适配各种货币类型的数字货币结构时,泛型就是你的首选工具。 | ||
|
||
## 泛型基础招式:声明与使用 | ||
|
||
### 1. 声明类型参数 | ||
|
||
- **函数中的泛型** | ||
|
||
函数的类型参数写在函数名之后、参数列表之前,格式是 `<T>`。 | ||
|
||
```move | ||
fun id<T>(x: T): T { | ||
(x: T) // 返回原值,类型保持不变 | ||
} | ||
``` | ||
|
||
在这个例子中,`id`函数无论你传入什么类型,它都会原封不动地返回给你。这就是“类型不设限”的神奇之处。 | ||
|
||
- **结构体中的泛型** | ||
|
||
结构体的类型参数写在结构体名之后。可以声明单个或多个类型参数: | ||
|
||
```move | ||
struct Foo<T> has copy, drop { x: T } | ||
struct Bar<T1, T2> has copy, drop { x: T1, y: vector<T2> } | ||
``` | ||
|
||
注意,泛型参数可以不一定要用在字段上(稍后讲到)。 | ||
|
||
### 2. 调用泛型 | ||
|
||
泛型的用法灵活,Move的类型推导(Type Inference)常常能替你完成大部分工作: | ||
|
||
- **显式指定类型实参** | ||
|
||
```move | ||
let x = id<bool>(true); | ||
``` | ||
|
||
- **省略类型实参** | ||
|
||
```move | ||
let x = id(true); // Move会推导出 T 是 bool | ||
``` | ||
|
||
对结构体也是类似: | ||
|
||
```move | ||
let foo = Foo<bool> { x: true }; // 显式类型 | ||
let foo = Foo { x: true }; // 类型推导 | ||
``` | ||
|
||
不过,当类型推导“犹豫不决”时,你就得“手动指点迷津”。比如: | ||
|
||
```move | ||
let v = vector::new<u64>(); // 明确向量元素是 u64 | ||
``` | ||
|
||
## 泛型“注意事项”:不小心就是一个大坑 | ||
|
||
1. **类型实参不匹配** | ||
|
||
如果你传入的值和指定的类型不匹配,Move会立刻告诉你哪里出了问题: | ||
|
||
```move | ||
let x = id<u64>(true); // 错误!true 不是 u64 | ||
``` | ||
|
||
2. **类型推导失败** | ||
|
||
有时类型推导“超出了能力范围”,需要你帮它一把。例如: | ||
|
||
```move | ||
let v = vector::new(); // 错误!无法推导向量元素的类型 | ||
let v = vector::new<u64>(); // 手动标注解决问题 | ||
``` | ||
|
||
## 高级用法:Phantom类型参数 | ||
|
||
Move为了解决“未使用类型参数”的问题,引入了Phantom类型参数。这些类型参数虽然存在,但不会影响结构体的能力派生。可以通过关键字 `phantom` 来声明: | ||
|
||
```move | ||
struct Coin<phantom Currency> has store { | ||
value: u64 | ||
} | ||
``` | ||
|
||
这样一来,即使 `Currency` 没有 `store` 能力,`Coin` 依然可以放入全局存储。此外,Phantom类型参数还可以加上能力约束,进一步增强其灵活性: | ||
|
||
```move | ||
struct S<phantom T: copy> {} | ||
``` | ||
|
||
## 泛型与能力:约束是关键 | ||
|
||
泛型的默认状态是“无所不能”,但这可能导致“乱用魔法”的情况。为了更安全,可以通过能力约束(Constraints)限制泛型类型参数的使用: | ||
|
||
```move | ||
T: copy // 只能用具有 copy 能力的类型 | ||
T: copy + drop // 同时需要 copy 和 drop 能力 | ||
``` | ||
|
||
约束会在调用点进行验证。例如: | ||
|
||
```move | ||
struct Foo<T: key> { x: T } | ||
let foo = Foo<u8> { x: 42 }; // 错误!u8 没有 key 能力 | ||
``` | ||
|
||
## 不要试图挑战Move的底线:递归限制 | ||
|
||
Move对泛型递归进行了严格的限制,避免“无穷类型”导致编译器卡死。例如: | ||
|
||
```move | ||
struct A<T> {} | ||
fun foo<T>() { | ||
foo<A<T>>(); // 错误!会生成无限多的类型 | ||
} | ||
``` | ||
|
||
但有限递归是允许的: | ||
|
||
```move | ||
fun foo<T>() { | ||
foo<A<u64>>(); // 合法!只有有限多的类型 | ||
} | ||
``` | ||
|
||
## 进阶应用:使用泛型构建“支持多种资产”的DeFi协议 | ||
|
||
构建一个支持多种资产的去中心化金融 (DeFi) 协议,需要让资产类型在智能合约中通用并安全地管理。Move的泛型特性非常适合这一场景,通过类型参数和能力约束,可以确保灵活性与安全性的统一。以下是一个设计和实现方案: | ||
|
||
### 1. 设计思路:多资产管理的关键需求 | ||
|
||
- **灵活性**:支持任意符合条件的资产(如不同的代币类型)。 | ||
- **安全性**:资产操作必须遵循能力约束(如只能操作具有 store 或 key 能力的资产)。 | ||
- **可扩展性**:协议支持添加新资产类型,而无需修改已有代码。 | ||
|
||
Move中的泛型使我们能够设计一套通用的接口来满足这些需求,同时通过能力约束保证安全性。 | ||
|
||
### 2. 构建核心模块:多资产存款与取款 | ||
|
||
**定义多资产通用结构** | ||
|
||
通过泛型参数 `Asset` 表示不同资产类型,使用能力约束确保资产的安全存储。 | ||
|
||
```move | ||
module DeFi { | ||
struct Vault<Asset: store> has key, store { | ||
balance: u64, | ||
} | ||
// 初始化一个资产金库 | ||
public fun initialize_vault<Asset: store>(account: &signer): Vault<Asset> { | ||
Vault { balance: 0 } | ||
} | ||
// 存款功能 | ||
public fun deposit<Asset: store>( | ||
vault: &mut Vault<Asset>, | ||
amount: u64 | ||
) { | ||
vault.balance = vault.balance + amount; | ||
} | ||
// 取款功能 | ||
public fun withdraw<Asset: store>( | ||
vault: &mut Vault<Asset>, | ||
amount: u64 | ||
) acquires Vault { | ||
assert!(vault.balance >= amount, 1); | ||
vault.balance = vault.balance - amount; | ||
} | ||
// 查询余额 | ||
public fun get_balance<Asset: store>(vault: &Vault<Asset>): u64 { | ||
vault.balance | ||
} | ||
} | ||
``` | ||
|
||
### 3. 支持多种资产操作 | ||
|
||
**为了让协议支持多种资产类型,我们可以使用 Move 的泛型实现“单金库多资产”的设计。以下是示例扩展**: | ||
|
||
**扩展支持资产类型** | ||
|
||
使用幻影类型参数 (Phantom Type Parameters) 区分不同资产。 | ||
|
||
```move | ||
module MultiAssetDeFi { | ||
use DeFi; | ||
struct Token<phantom Asset> has key, store { | ||
total_supply: u64, | ||
} | ||
public fun initialize_token<Asset: store>(supply: u64): Token<Asset> { | ||
Token { total_supply: supply } | ||
} | ||
public fun transfer<Asset: store>( | ||
token: &mut Token<Asset>, | ||
amount: u64 | ||
) { | ||
assert!(token.total_supply >= amount, 2); | ||
token.total_supply = token.total_supply - amount; | ||
} | ||
} | ||
``` | ||
|
||
**示例:支持多个货币** | ||
|
||
比如,定义 ETH 和 BTC 两种资产,分别创建相应的 Vault: | ||
|
||
```move | ||
module Example { | ||
use DeFi; | ||
use MultiAssetDeFi; | ||
// 定义 ETH 和 BTC 两种资产类型 | ||
struct ETH has key, store {} | ||
struct BTC has key, store {} | ||
public fun main() { | ||
// 初始化两个金库 | ||
let eth_vault = DeFi::initialize_vault<ETH>(); | ||
let btc_vault = DeFi::initialize_vault<BTC>(); | ||
// 对金库进行操作 | ||
DeFi::deposit<ETH>(&mut eth_vault, 100); | ||
DeFi::deposit<BTC>(&mut btc_vault, 200); | ||
let eth_balance = DeFi::get_balance<ETH>(ð_vault); | ||
let btc_balance = DeFi::get_balance<BTC>(&btc_vault); | ||
assert!(eth_balance == 100, 3); | ||
assert!(btc_balance == 200, 4); | ||
} | ||
} | ||
``` | ||
|
||
### 4. 提升灵活性:支持更多功能 | ||
|
||
**跨资产交易** | ||
|
||
通过泛型和能力约束实现跨资产的安全兑换逻辑: | ||
|
||
```move | ||
module Swap { | ||
use DeFi; | ||
public fun swap<Asset1: store, Asset2: store>( | ||
vault1: &mut DeFi::Vault<Asset1>, | ||
vault2: &mut DeFi::Vault<Asset2>, | ||
amount1: u64, | ||
amount2: u64 | ||
) { | ||
assert!(vault1.balance >= amount1, 5); | ||
assert!(vault2.balance >= amount2, 6); | ||
// 执行资产交换 | ||
vault1.balance = vault1.balance - amount1; | ||
vault2.balance = vault2.balance - amount2; | ||
vault1.balance = vault1.balance + amount2; | ||
vault2.balance = vault2.balance + amount1; | ||
} | ||
} | ||
``` | ||
|
||
**事件记录** | ||
|
||
通过事件机制记录资产存取与交易行为,方便用户审计和调试: | ||
|
||
```move | ||
module EventLogger { | ||
struct DepositEvent has store { asset: vector<u8>, amount: u64 } | ||
struct WithdrawEvent has store { asset: vector<u8>, amount: u64 } | ||
public fun log_deposit<Asset: store>( | ||
asset_name: vector<u8>, | ||
amount: u64 | ||
) { | ||
emit DepositEvent { asset: asset_name, amount }; | ||
} | ||
public fun log_withdraw<Asset: store>( | ||
asset_name: vector<u8>, | ||
amount: u64 | ||
) { | ||
emit WithdrawEvent { asset: asset_name, amount }; | ||
} | ||
} | ||
``` | ||
|
||
### 5. 安全性与扩展性注意事项 | ||
|
||
1. **能力约束**: | ||
- 确保只有具有 store 或 key 能力的类型可以用于资产管理,避免不可序列化类型被误用。 | ||
- 在操作中通过能力约束避免资源丢失或泄露。 | ||
2. **代码复用**: | ||
使用泛型提高复用性,但注意避免过度复杂化(如不必要的递归或嵌套泛型)。 | ||
3. **拓展机制**: | ||
可通过额外模块添加新资产类型或功能,如支持 ERC-20、NFT 等不同的代币标准。 | ||
|
||
通过上述设计,协议可以灵活支持多种资产,同时保持高度安全性和扩展性,满足多资产DeFi系统的需求。 | ||
|
||
## 结语:玩转泛型,化繁为简 | ||
|
||
学习Move的泛型就像在掌握一门艺术:它能让你的代码更加优雅、通用,但也需要谨慎使用,避免掉入“类型地狱”。无论是通过Phantom参数增强灵活性,还是用能力约束提升安全性,泛型都为开发者提供了强大的工具。 | ||
|
||
> **请用微信关注《HOH水分子》公众号,我们将持续分享和制作变成语言教程,让大家对编程产生化学反应。** | ||
![水分子社区](../images/HOH_QR.jpg) |