Skip to content

Commit

Permalink
群里分享学习笔记_共学营+对外输出学习笔记_共学营
Browse files Browse the repository at this point in the history
  • Loading branch information
lizhe committed Nov 23, 2024
1 parent cd1314e commit 83d0f0a
Show file tree
Hide file tree
Showing 2 changed files with 325 additions and 0 deletions.
2 changes: 2 additions & 0 deletions mover/lizhecome/co-learn-2411/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- [x] (八)Move语言的函数【[学习笔记链接](https://github.com/move-cn/letsmove/tree/main/mover/lizhecome/notes/08_move_function.md)
- [x] (九)Move语言中的能力(Ability)【[学习笔记链接](https://github.com/move-cn/letsmove/tree/main/mover/lizhecome/notes/09_move_ability.md)
- [x] (十)让Move语言的单元测试跑起来!【[学习笔记链接](https://github.com/move-cn/letsmove/tree/main/mover/lizhecome/notes/10_move_unit_test.md)
- [x] (十一)Move泛型:从入门到“我真的会了”【[学习笔记链接](https://github.com/move-cn/letsmove/tree/main/mover/lizhecome/notes/11_move_generics.md)


## 对外输出学习笔记
Expand All @@ -52,6 +53,7 @@
- [x] (八)Move语言的函数【[学习笔记链接](https://learnblockchain.cn/article/9942)
- [x] (九)Move语言中的能力(Ability)【[学习笔记链接](https://learnblockchain.cn/article/9982)
- [x] (十)让Move语言的单元测试跑起来!【[学习笔记链接](https://learnblockchain.cn/article/10011)
- [x] (十一)Move泛型:从入门到“我真的会了”【[学习笔记链接](https://learnblockchain.cn/article/10019)

## 在HOH社区公众号发布自己的技术文章

Expand Down
323 changes: 323 additions & 0 deletions mover/lizhecome/notes/11_move_generics.md
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>(&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)

0 comments on commit 83d0f0a

Please sign in to comment.