Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1.1 Move 语言简介

什么是 Aptos Move?

Aptos Move 是一种专门为区块链设计的编程语言,由 Facebook(现 Meta)开发,最初为 Diem 项目设计。Aptos Move 语言的核心设计理念是资源安全类型安全

现由 Aptos 区块链使用,并扩展语法并重写编译器和升级虚拟机

Move 语言的特点

🔒 资源安全

  • 资源模型:Move 将数字资产视为"资源",确保资产不能被复制或意外销毁
  • 线性类型系统:每个资源只能被移动,不能被复制
  • 所有权模型:明确的资源所有权和转移机制

🛡️ 类型安全

  • 静态类型检查:编译时进行类型检查,避免运行时错误
  • 内存安全:没有空指针、悬空引用等内存安全问题
  • 形式化验证:支持数学证明程序正确性

📦 模块化设计

  • Module 系统:代码组织在模块中,便于管理和重用
  • 能力系统:通过能力(Abilities)控制类型的行为
  • 泛型支持:支持泛型编程,提高代码复用性

Move 与其他语言的对比

特性MoveSolidityRust
资源安全✅ 原生支持❌ 需要手动管理✅ 所有权系统
类型安全✅ 静态类型⚠️ 动态类型✅ 静态类型
区块链专用✅ 专为区块链设计✅ 以太坊专用❌ 通用语言
学习曲线🟡 中等🟢 简单🔴 陡峭

Move 的应用场景

🪙 代币合约

// 简单的代币合约示例
module my_addr::basic_coin {
    struct Coin has key {
        value: u64,
    }
    
    public fun mint(account: &signer, value: u64) {
        move_to(account, Coin { value })
    }
}

🏛️ DeFi 协议

  • 去中心化交易所(DEX)
  • 借贷协议
  • 流动性挖矿
  • 衍生品合约

Move 生态系统

🛠️ 开发工具

  • Aptos CLI:命令行工具
  • Move Analyzer:vscode 插件
  • Move Prover:形式化验证工具
  • IDE 插件:VS Code 等编辑器支持

为什么选择 Move?

✅ 优势

  1. 安全性:资源模型天然防止资产丢失
  2. 可验证性:支持形式化验证
  3. 可升级性:支持模块升级

⚠️ 挑战

  1. 学习曲线:资源模型概念较新
  2. 工具链:相比 Solidity 工具链不够成熟

学习路径建议

🎯 初学者路径

  1. 基础概念:理解资源、模块、能力
  2. 语法学习:掌握基本语法和数据类型
  3. 简单项目:编写简单的代币合约
  4. 进阶特性:学习泛型、错误处理
  5. 实战项目:构建完整的 DeFi 协议

📚 推荐资源

小结

Move 语言通过其独特的资源模型和类型系统,为区块链开发提供了更高的安全性和可靠性。虽然学习曲线相对较陡,但掌握 Move 将让你在区块链开发领域具有独特优势。

在接下来的章节中,我们将从最基础的 module 开始,逐步深入学习 Move 语言的各个方面。


下一节1.2 开发环境搭建

1.2 开发环境搭建

环境要求

在开始学习 Move 之前,我们需要搭建一个完整的开发环境。

我们需要使用 Aptos cli 作为编译器和执行环境

🛠️ 必需工具

  • Git:版本控制
  • Rust:Move 工具链依赖

安装步骤

1. 安装 Aptos CLI

本文列出目前几种安装方式,更多方式详情可以查看文档:

https://aptos.dev/build/cli

Mac

  1. 使用 Homebrew 安装
brew install aptos
  1. 使用 Shell 脚本安装
curl -fsSL "https://aptos.dev/scripts/install_cli.sh" | sh

Linux

  1. 使用 Shell 脚本安装
curl -fsSL "https://aptos.dev/scripts/install_cli.sh" | sh
  1. ArchLinux 用户可以使用 AUR 安装
yay -S aptos-cli

Windows

  1. winget 安装
winget install aptos
  1. 使用 Chocolatey 安装
choco install aptos
  1. 使用 scoop 安装
scoop install https://aptos.dev/scoop/aptos.json
  1. 使用 PowerShell 脚本安装
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser; iwr https://aptos.dev/scripts/install_cli.ps1 | iex

验证安装

aptos --version

显示类似以下内容,表示安装成功(版本 7.6.0):

aptos 7.6.0

2. 安装 IDE 和插件

VS Code(推荐)

  1. 下载并安装 VS Code
  2. 安装以下插件:
    • Move on Aptos:Aptos Move 支持,语法高亮和代码补全
      • https://marketplace.visualstudio.com/items?itemName=AptosLabs.move-on-aptos

其他 IDE 选项

  • IntelliJ IDEA:Move on Aptos
    • https://plugins.jetbrains.com/plugin/14721-move-on-aptos

下一步

环境搭建完成后,我们就可以开始编写第一个 Aptos Move 程序了!

📋 检查清单

  • Aptos CLI 安装成功
  • IDE 配置完成

下一节1.3 第一个 Aptos Move 程序

1.3 第一个 Aptos Move 程序

创建 Hello World 程序

让我们从最经典的 "Hello World" 程序开始,了解 Aptos Move 的基本语法结构。

项目结构

首先创建一个新的 Aptos Move 项目:

# 创建项目
mkdir 01-hello_blockchain
cd 01-hello_blockchain
aptos move init --name hello-blockchain  --template hello-blockchain

这将创建一个名为 01-hello_blockchain 的目录,包含以下结构:

├── Move.toml
├── .gitignore
├── sources
│   └── hello_blockchain.move
└── tests

### 查看第一个模块

在 `sources/` 目录下打开 `hello_blockchain.move` 文件:

```move
module hello_blockchain::message {
    use std::error;
    use std::signer;
    use std::string;
    use aptos_framework::event;
    #[test_only]
    use std::debug;

    //:!:>resource
    struct MessageHolder has key {
        message: string::String,
    }
    //<:!:resource

    #[event]
    struct MessageChange has drop, store {
        account: address,
        from_message: string::String,
        to_message: string::String,
    }

    /// There is no message present
    const ENO_MESSAGE: u64 = 0;

    #[view]
    public fun get_message(addr: address): string::String acquires MessageHolder {
        assert!(exists<MessageHolder>(addr), error::not_found(ENO_MESSAGE));
        borrow_global<MessageHolder>(addr).message
    }

    public entry fun set_message(account: signer, message: string::String)
    acquires MessageHolder {
        let account_addr = signer::address_of(&account);
        if (!exists<MessageHolder>(account_addr)) {
            move_to(&account, MessageHolder {
                message,
            })
        } else {
            let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
            let from_message = old_message_holder.message;
            event::emit(MessageChange {
                account: account_addr,
                from_message,
                to_message: copy message,
            });
            old_message_holder.message = message;
        }
    }

    #[test(account = @0x1)]
    public entry fun sender_can_set_message(account: signer) acquires MessageHolder {
        let msg: string::String = string::utf8(b"Running test for sender_can_set_message...");
        debug::print(&msg);

        let addr = signer::address_of(&account);
        aptos_framework::account::create_account_for_test(addr);
        set_message(account, string::utf8(b"Hello, Blockchain"));

        assert!(
            get_message(addr) == string::utf8(b"Hello, Blockchain"),
            ENO_MESSAGE
        );
    }
}

代码解析

让我们逐行分析这个简单的 Move 程序:

1. 模块声明

module hello_blockchain::message {
  • module:关键字,声明这是一个模块
  • hello_blockchain::message:模块的完整名称
    • hello_blockchain:地址(在 Move.toml 中定义)
    • message:模块名称

2. 导入标准库

use std::error;
use std::signer;
use std::string;
use aptos_framework::event;
#[test_only]
use std::debug;
  • use:导入关键字
  • std::error:标准库中的错误处理模块
  • std::signer:处理签名者的模块
  • std::string:字符串处理模块
  • aptos_framework::event:Aptos 框架中的事件模块
  • #[test_only]:仅在测试环境中使用的模块

3. 资源定义

struct MessageHolder has key {
    message: string::String,
}
//<:!:resource

#[event]
struct MessageChange has drop, store {
    account: address,
    from_message: string::String,
    to_message: string::String,
}
  • struct:定义一个结构体

  • MessageHolder:存储消息的资源

  • has key:具有 Key 能力,用于全局存储

  • message: string::String:消息内容

  • #[event]:声明这是一个事件结构体

  • MessageChange:事件结构体,记录消息变更

  • account: address:事件触发的账户地址

  • from_message: string::String:变更前的消息

  • to_message: string::String:变更后的消息

4. 常量定义

const ENO_MESSAGE: u64 = 0;
  • const:声明一个常量
  • ENO_MESSAGE:表示没有消息的错误代码

5. 函数定义

#[view]
public fun get_message(addr: address): string::String acquires MessageHolder 

public entry fun set_message(account: signer, message: string::String)
acquires MessageHolder 
  • public:访问修饰符,表示函数可以被其他模块调用

  • fun:关键字,声明这是一个函数

  • get_message:函数名

  • (addr: address):参数列表

  • : string::String:返回类型

  • acquires MessageHolder:声明函数需要获取 MessageHolder 资源

  • {}:函数体

  • entry: 表示这是一个入口函数,可以从链下调用

  • account: signer:签名者账户

  • message: string::String:要设置的消息内容

  • acquires MessageHolder:同样声明需要获取 MessageHolder 资源

6. 函数实现

assert!(exists<MessageHolder>(addr), error::not_found(ENO_MESSAGE));
borrow_global<MessageHolder>(addr).message
  • assert!:断言函数,检查条件是否为真
  • exists<MessageHolder>(addr):检查地址是否存在 MessageHolder 资源
  • error::not_found(ENO_MESSAGE):如果不存在,抛出错误
  • borrow_global<MessageHolder>(addr).message:获取并返回消息内容
if (!exists<MessageHolder>(account_addr)) {
    move_to(&account, MessageHolder {
        message,
    })
} else {
    let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
    let from_message = old_message_holder.message;
    event::emit(MessageChange {
        account: account_addr,
        from_message,
        to_message: copy message,
    });
    old_message_holder.message = message;
}
  • if (!exists<MessageHolder>(account_addr)):检查是否存在 MessageHolder
  • move_to(&account, MessageHolder { message }):如果不存在,创建新的 MessageHolder
  • else 分支:如果存在,获取现有的 MessageHolder
  • event::emit(MessageChange { ... }):触发消息变更事件
  • old_message_holder.message = message:更新消息内容

7. 测试函数

#[test(account = @0x1)]
public entry fun sender_can_set_message(account: signer) acquires MessageHolder {
    let msg: string::String = string::utf8(b"Running test for sender_can_set_message...");
    debug::print(&msg);
    let addr = signer::address_of(&account);
    aptos_framework::account::create_account_for_test(addr);
    set_message(account, string::utf8(b"Hello, Blockchain"));
    assert!(
        get_message(addr) == string::utf8(b"Hello, Blockchain"),
        ENO_MESSAGE
    );
}
  • #[test(account = @0x1)]:声明这是一个测试函数,并指定测试账户地址
  • public entry fun sender_can_set_message(account: signer)
  • let msg: string::String = string::utf8(b"Running test for sender_can_set_message...");:创建测试消息
  • debug::print(&msg);:打印调试信息
  • let addr = signer::address_of(&account);:获取签名者地址
  • aptos_framework::account::create_account_for_test(addr);:为测试创建账户
  • set_message(account, string::utf8(b"Hello, Blockchain"));:设置消息
  • assert!(get_message(addr) == string::utf8(b"Hello, Blockchain"), ENO_MESSAGE);:断言消息设置成功

编译和运行

1. 编译项目

aptos move build --named-addresses hello_blockchain=0x1234

2. 运行测试

aptos move test --named-addresses hello_blockchain=0x1234

Move 语法要点

1. 注释

// 单行注释
/// 文档注释
/* 多行注释 */

2. 变量声明

let x = 10;           // 类型推断
let y: u64 = 20;      // 显式类型声明

3. 条件语句

if (condition) {
    // 代码块
} else {
    // 代码块
}

4. 循环

while (condition) {
    // 循环体
}

5. 函数调用

module_name::function_name(arg1, arg2);

6. 断言

assert!(condition, error_code);

常见错误和调试

调试技巧

  • 使用 debug::print() 输出调试信息
  • 在测试中使用 assert!() 验证结果

小结

通过这个简单的 Hello World 程序,我们学习了:

  1. 模块结构:如何声明和组织模块
  2. 函数定义:如何定义和调用函数
  3. 测试编写:如何为代码编写测试
  4. 调试技巧:如何使用调试输出

下一节2.1 Module 概念与结构

2.1 Module 概念与结构

什么是 Module?

在 Move 中,Module(模块) 是代码组织的基本单位。模块类似于其他编程语言中的类或包,它封装了相关的函数、结构体和常量。

Module 的核心概念

1. 命名空间

module my_addr::math {
    // 模块内容
}
  • my_addr:地址(Address),类似于包名
  • math:模块名
  • my_addr::math:完整的模块标识符

2. 封装性

  • 模块内的代码可以访问模块内的所有内容
  • 其他模块只能访问被标记为 public 的内容
  • 提供了良好的封装和抽象

3. 可重用性

  • 模块可以被其他模块导入和使用
  • 支持模块的组合和扩展

Module 的基本结构

完整的模块示例

module my_addr::bank {
    use std::signer;
    use std::debug;
    
    // 错误码常量
    const EINSUFFICIENT_BALANCE: u64 = 1;
    const EINVALID_AMOUNT: u64 = 2;
    
    // 结构体定义
    struct Account has key {
        balance: u64,
        owner: address,
    }
    
    // 事件结构体
    #[event]
    struct DepositEvent has drop, store {
        account: address,
        amount: u64,
    }
    
    // 公共函数
    public fun create_account(account: &signer) {
        let account_addr = signer::address_of(account);
        move_to(account, Account {
            balance: 0,
            owner: account_addr,
        });
        debug::print(&b"Account created for: ");
        debug::print(&account_addr);
    }
    
    // 公共函数
    public fun deposit(account: &signer, amount: u64) acquires Account {
        assert!(amount > 0, EINVALID_AMOUNT);
        
        let account_addr = signer::address_of(account);
        let account_ref = &mut Account[account_addr];
        account_ref.balance += amount;
        
        debug::print(&b"Deposited: ");
        debug::print(&amount);
    }
    
    // 公共函数
    public fun withdraw(account: &signer, amount: u64): u64 acquires Account {
        assert!(amount > 0, EINVALID_AMOUNT);
        
        let account_addr = signer::address_of(account);
        let account_ref = &mut Account[account_addr];
        
        assert!(account_ref.balance >= amount, EINSUFFICIENT_BALANCE);
        account_ref.balance -= amount;
        
        debug::print(&b"Withdrawn: ");
        debug::print(&amount);
        amount
    }
    
    // 公共函数
    public fun get_balance(account_addr: address): u64 acquires Account{
        let account = &Account[account_addr];
        account.balance
    }
    
    // 私有函数(只能在模块内部调用)
    fun validate_amount(amount: u64): bool {
        amount > 0
    }
}

Module 的组成部分

1. 模块声明

module <address>::<module_name> {
    // 模块内容
}

2. 导入语句

use std::signer;      // 导入标准库模块
use my_addr::math;    // 导入自定义模块

3. 常量定义

常量定义只能定义基础类型变量

const <name>: <type> = <value>;

const ERROR_CODE: u64 = 1;
const MAX_BALANCE: u64 = 1000000;

4. 结构体定义

struct MyStruct has key, store {
    field1: u64,
    field2: vector<u8>,
}

5. 函数定义

public fun public_function() {
    // 公共函数
}

fun private_function() {
    // 私有函数
}

Module 的访问控制

访问修饰符

1. public

public fun public_function() {
    // 可以被其他模块调用
}

2. 默认(私有)

fun private_function() {
    // 只能在模块内部调用
}

3. public(friend)

需要在模块声明中指定友元模块

public(friend) fun friend_function() {
    // 只能被友元模块调用
}

4. public(package)

无需声明 Friend 模块,当前包内可以直接访问

public (package) fun package_function() {
    // 只能被同一包内的模块调用
}

访问控制示例

module my_addr::access_control {
    use std::debug;
    
    // 友元模块
    friend my_addr::other_module;
    
    // 公共函数 - 任何模块都可以调用
    public fun public_function() {
        debug::print(&b"This is public");
    }
    
    // 私有函数 - 只能在模块内部调用
    fun private_function() {
        debug::print(&b"This is private");
    }
    
    // 公共函数调用私有函数
    public fun call_private() {
        private_function(); // 可以调用私有函数
    }

    // 友元函数 - 只能被指定模块调用
    public(friend) fun friend_function() {
        debug::print(&b"This is a friend function");
    }

    // 包内函数 - 只能被同一包内的模块调用
    public(package) fun package_function() {
        debug::print(&b"This is a package function");
    }
}

// 另一个模块
module my_addr::other_module {
    use my_addr::access_control;
    
    fun test_access() {
        access_control::public_function(); // ✅ 可以调用
        // access_control::private_function(); // ❌ 不能调用私有函数
        access_control::call_private(); // ✅ 可以调用
    }
}

Module 的组织原则

1. 单一职责原则

每个模块应该只负责一个特定的功能领域

// ✅ 好的设计
module my_addr::math {
    // 数学运算相关
}

module my_addr::string_utils {
    // 字符串处理相关
}

// ❌ 不好的设计
module my_addr::everything {
    // 混合了多种功能
}

2. 高内聚,低耦合

模块内部的功能应该紧密相关,模块之间的依赖应该尽量减少

// 高内聚的模块设计
module my_addr::user_management {
    struct User has key, store {
        id: u64,
        name: vector<u8>,
        email: vector<u8>,
    }
    
    public fun create_user(account: &signer, name: vector<u8>, email: vector<u8>) {
        // 用户创建逻辑
    }
    
    public fun update_user(account: &signer, name: vector<u8>) {
        // 用户更新逻辑
    }
    
    public fun delete_user(account: &signer) {
        // 用户删除逻辑
    }
}

3. 模块依赖管理

使用 use 语句导入其他模块

// 基础模块
module my_addr::math {
    public fun add(a: u64, b: u64): u64 { a + b }
    public fun subtract(a: u64, b: u64): u64 { a - b }
}

// 依赖基础模块的高级模块
module my_addr::calculator {
    use my_addr::math;
    
    public fun calculate(a: u64, b: u64, operation: u8): u64 {
        if (operation == 1) {
            math::add(a, b)
        } else {
            math::subtract(a, b)
        }
    }
}

模块设计模式

1. 工具模块模式

只使用当前函数,不需要状态或存储

module my_addr::utils {
    use std::vector;
    
    public fun find_max(numbers: vector<u64>): u64 {
        let max = 0;
        let i = 0;
        let len = vector::length(&numbers);
        
        while (i < len) {
            let current = *vector::borrow(&numbers, i);
            if (current > max) {
                max = current;
            };
            i = i + 1;
        };
        max
    }
    
    public fun reverse_string(s: vector<u8>): vector<u8> {
        let result = vector::empty<u8>();
        let i = vector::length(&s);
        
        while (i > 0) {
            i = i - 1;
            vector::push_back(&mut result, *vector::borrow(&s, i));
        };
        result
    }
}

2. 服务模块模式

提供状态和存储,通常用于管理复杂的业务逻辑

module my_addr::voting_service {
    use std::signer;
    use std::vector;
    
    struct Vote has key, store {
        proposal_id: u64,
        voter: address,
        choice: bool,
    }
    
    struct Proposal has key, store {
        id: u64,
        title: vector<u8>,
        description: vector<u8>,
        yes_votes: u64,
        no_votes: u64,
        is_active: bool,
    }
    
    public fun create_proposal(
        account: &signer,
        title: vector<u8>,
        description: vector<u8>
    ) {
        // 创建提案逻辑
    }
    
    public fun vote(
        account: &signer,
        proposal_id: u64,
        choice: bool
    ) {
        // 投票逻辑
    }
    
    public fun get_proposal_result(proposal_id: u64): (u64, u64) {
        // 获取投票结果
        (0, 0) // 占位符
    }
}

最佳实践

1. 命名规范

// ✅ 好的命名
module my_addr::user_management { }
module my_addr::token_contract { }
module my_addr::voting_system { }

// ❌ 不好的命名
module my_addr::user { }
module my_addr::token { }
module my_addr::vote { }

2. 文档注释

vscode 插件可以读取模块中的文档注释

module my_addr::math_utils {

    /// 计算两个数的最大公约数
    /// @param a 第一个数
    /// @param b 第二个数
    /// @return 最大公约数
    public fun gcd(a: u64, b: u64): u64 {
        if (b == 0) {
            a
        } else {
            gcd(b, a % b)
        }
    }
}

3. 错误处理

使用 assert!() 进行错误处理 或者 使用 abort 的方式

module my_addr::safe_math {
    const EOVERFLOW: u64 = 1;
    const EINVALID_INPUT: u64 = 2;
    
    public fun safe_add(a: u64, b: u64): u64 {
        assert!(a <= 0xFFFFFFFFFFFFFFFF - b, EOVERFLOW);
        a + b
    }

    public fun safe_div(a: u64, b: u64): u64 {
        if (a == 0) {
            abort EINVALID_INPUT;
        }
        a / b
    }
}

小结

通过本章的学习,我们了解了:

  1. Module 的概念:Move 中代码组织的基本单位
  2. Module 的结构:声明、导入、常量、结构体、函数
  3. 访问控制:public、private、friend、package 访问修饰符
  4. 最佳实践:命名、文档、错误处理

Module 是 Move 编程的基础,掌握好模块的设计和组织,将为后续学习更复杂的 Move 概念打下坚实基础。


下一节3.1 data-types

3.1 基本数据类型

Move 的基本数据类型

Move 是一种静态类型语言,所有变量和表达式都有明确的类型。了解基本数据类型是编写 Move 程序的基础。

整数类型

无符号整数

Move 提供了多种无符号整数类型,用于表示不同范围的整数值:

module my_addr::integer_types {
    use std::debug;
    
    #[test]
    public fun demonstrate_integers() {
        // u8: 8位无符号整数 (0 到 255)
        let small_number: u8 = 255;
        debug::print(&small_number);
        
        // u16: 16位无符号整数 (0 到 65535)
        let medium_number: u16 = 65535;
        debug::print(&medium_number);
        
        // u32: 32位无符号整数 (0 到 4,294,967,295)
        let large_number: u32 = 4294967295;
        debug::print(&large_number);
        
        // u64: 64位无符号整数 (0 到 18,446,744,073,709,551,615)
        let huge_number: u64 = 18446744073709551615;
        debug::print(&huge_number);
        
        // u128: 128位无符号整数
        let massive_number: u128 = 340282366920938463463374607431768211455;
        debug::print(&massive_number);
        
        // u256: 256位无符号整数
        let enormous_number: u256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
        debug::print(&enormous_number);
    }
    
    // 整数运算示例
    #[test]
    public fun arithmetic_operations() {
        let a: u64 = 10;
        let b: u64 = 3;
        
        // 加法
        let sum = a + b;
        debug::print(&sum); // 13
        assert!(sum == 13, 100);
        
        // 减法
        let difference = a - b;
        debug::print(&difference); // 7
        assert!(difference == 7, 101);
        
        // 乘法
        let product = a * b;
        debug::print(&product); // 30
        assert!(product == 30, 102);
        
        // 除法(整数除法)
        let quotient = a / b;
        debug::print(&quotient); // 3
        assert!(quotient == 3, 103);
        
        // 取余
        let remainder = a % b;
        debug::print(&remainder); // 1
        assert!(remainder == 1, 104);
    }
    
    // 整数比较
    #[test]
    public fun comparison_operations() {
        let a: u64 = 10;
        let b: u64 = 5;
        
        let is_equal = a == b;        // false
        let is_not_equal = a != b;    // true
        let is_greater = a > b;       // true
        let is_less = a < b;          // false
        let is_greater_equal = a >= b; // true
        let is_less_equal = a <= b;   // false

        debug::print(&is_equal);
        debug::print(&is_not_equal);
        debug::print(&is_greater);
        debug::print(&is_less);
        debug::print(&is_greater_equal);
        debug::print(&is_less_equal);

        assert!(is_equal == false, 200);
        assert!(is_not_equal == true, 201);
        assert!(is_greater == true, 202);
        assert!(is_less == false, 203);
        assert!(is_greater_equal == true, 204);
        assert!(is_less_equal == false, 205);
    }
}

整数类型选择指南

类型范围用途
u80-255小数值、状态标志
u160-65535中等数值
u320-4,294,967,295一般用途
u640-18,446,744,073,709,551,615推荐使用
u128极大数值金融计算、大数运算
u256极大数值密码学、精确计算

布尔类型

布尔类型用于表示逻辑值:

module my_addr::boolean_types {
    use std::debug;
    
    #[test]
    public fun demonstrate_booleans() {
        // 布尔字面量
        let true_value: bool = true;
        let false_value: bool = false;
        debug::print(&true_value);
        debug::print(&false_value);
        assert!(true_value == true, 300);
        assert!(false_value == false, 301);

        // 布尔运算
        let a = true;
        let b = false;

        let and_result = a && b;  // false
        let or_result = a || b;   // true
        let not_result = !a;      // false
        debug::print(&and_result);
        debug::print(&or_result);
        debug::print(&not_result);
        assert!(and_result == false, 302);
        assert!(or_result == true, 303);
        assert!(not_result == false, 304);

        // 条件表达式
        let condition = 10 > 5;
        let result = if (condition) { true } else { false };
        debug::print(&result);
        assert!(result == true, 305);
    }
    
    // 布尔函数示例
    public fun is_even(number: u64): bool {
        number % 2 == 0
    }
    
    public fun is_positive(number: u64): bool {
        number > 0
    }
    
    public fun is_in_range(value: u64, min: u64, max: u64): bool {
        value >= min && value <= max
    }
}

地址类型

地址类型用于表示区块链上的账户地址:

module my_addr::address_types {
    use std::debug;
    use std::signer;
    
    #[test(account = @0x1234)]
    public fun demonstrate_addresses(account: &signer) {
        // 获取签名者地址
        let account_addr = signer::address_of(account);
        debug::print(&account_addr);
        assert!(account_addr == @0x1234, 400);

        // 地址字面量
        let my_address: address = @0x1234;
        let std_address: address = @0x1;
        debug::print(&my_address);
        debug::print(&std_address);
        assert!(my_address == @0x1234, 401);
        assert!(std_address == @0x1, 402);

        // 地址比较
        let is_my_address = account_addr == my_address;
        debug::print(&is_my_address);
        assert!(is_my_address == true, 403);
    }
    
    // 地址验证函数
    public fun is_valid_address(addr: address): bool {
        // 检查地址是否为零地址
        addr != @0x0
    }
    
    public fun is_my_address(addr: address): bool {
        addr == @0x1234
    }
}

向量类型

向量是 Move 中的动态数组:

module my_addr::vector_types {
    use std::debug;
    use std::vector;
    
    #[test]
    public fun demonstrate_vectors() {
        // 创建空向量
        let empty_vector: vector<u64> = vector::empty<u64>();
        assert!(empty_vector.length() == 0, 500);

        // 创建带初始值的向量
        let numbers: vector<u64> = vector[1, 2, 3, 4, 5];
        assert!(numbers.length() == 5, 501);

        // 添加元素
        numbers.push_back(6);
        assert!(numbers.length() == 6, 502);

        // 获取向量长度
        let length = numbers.length();
        debug::print(&length); // 6
        assert!(length == 6, 503);

        // 访问元素
        let first = numbers[0];
        let last = numbers[length - 1];
        debug::print(&first); // 1
        debug::print(&last);  // 6
        assert!(first == 1, 504);
        assert!(last == 6, 505);

        // 修改元素
        let mut_ref = numbers.borrow_mut(0);
        *mut_ref = 10;
        assert!(numbers[0] == 10, 506);

        // 删除元素
        let removed = numbers.pop_back();
        debug::print(&removed); // 6
        assert!(removed == 6, 507);
        assert!(numbers.length() == 5, 508);

        // 检查向量是否为空
        let is_empty = numbers.is_empty();
        debug::print(&is_empty); // false
        assert!(is_empty == false, 509);

        // 反转向量
        numbers.reverse();
        debug::print(&numbers); // [5, 4, 3, 2, 10]
        assert!(numbers == vector[5, 4, 3, 2, 10], 510);

        // 过滤向量
        let filtered_numbers = numbers.filter(|x| *x > 2);
        debug::print(&filtered_numbers); // [5, 4, 3, 10]
        assert!(filtered_numbers == vector[5, 4, 3, 10], 511);

        // 映射向量
        let mapped_numbers = numbers.map(|x| x * 2);
        debug::print(&mapped_numbers); // [10, 8, 6, 4, 20]
        assert!(mapped_numbers == vector[10, 8, 6, 4, 20], 512);

        // 折叠向量
        let sum = numbers.fold(0, |acc, x| acc + x);
        debug::print(&sum); // 24
        assert!(sum == 24, 513);

    }
}

字节向量(字符串)

在 Move 中,字符串实际上是字节向量:

module my_addr::string_types {
    use std::debug;
    use std::string;
    
    #[test]
    public fun demonstrate_strings() {
        // 使用 std::string 处理字符串
        let hello = string::utf8(b"Hello, Move!");
        let empty = string::utf8(b"");
        debug::print(&hello);
        debug::print(&empty);
        assert!(hello.length() == 12, 600);
        assert!(empty.length() == 0, 601);

        // 字符串长度
        let length = hello.length();
        debug::print(&length); // 12
        assert!(length == 12, 602);

        // 子字符串查找
        let move_str = string::utf8(b"Move");
        let move_index = hello.index_of(&move_str);
        debug::print(&move_index); // 7
        assert!(move_index == 7, 603);

        // 字符串连接
        let hello_str = string::utf8(b" Hello");
        hello_str.append_utf8(b" World!");
        debug::print(&hello_str); // "Hello World!"
        assert!(hello_str == string::utf8(b" Hello World!"), 604);
    }
}

类型转换

Move 是强类型语言,但支持一些隐式和显式类型转换:

module my_addr::type_conversion {
    use std::debug;
    
    #[test]
    public fun demonstrate_conversions() {
        // 显式转换(小类型到大类型)
        let small: u8 = 255;

        let medium: u16 = (small as u16);  // u8 -> u16
        let large: u32 = (medium as u32);  // u16 -> u32
        let huge: u64 = (large as u64);    // u32 -> u64

        assert!(medium == 255, 700);
        assert!(large == 255, 701);
        assert!(huge == 255, 702);

        // 显式转换(大类型到小类型)
        let big_number: u64 = 255;
        let small_number: u8 = (big_number as u8);
        debug::print(&small_number); // 255
        assert!(small_number == 255, 703);
    }

    #[test]
    #[expected_failure]
    fun error_handling() {
        // 尝试将一个大类型转换为小类型,Move 会报错
        let large_value: u64 = 300;
        let small_value: u8 = (large_value as u8); // 在此处发生错误
        debug::print(&small_value); // 44
    }
}

类型推断

Move 支持类型推断,编译器可以自动推断变量类型:

module my_addr::type_inference {
    use std::debug;
    
    public fun demonstrate_inference() {
        // 类型推断
        let number = 42;           // 推断为 u64
        let flag = true;           // 推断为 bool
        let text = b"Hello";       // 推断为 vector<u8>
        let list = vector[1, 2, 3]; // 推断为 vector<u64>
        
        // 显式类型声明
        let explicit_number: u64 = 42;
        let explicit_flag: bool = true;
        
        debug::print(&number);
        debug::print(&flag);
        debug::print(&text);
        debug::print(&list);
    }
}

常见错误和最佳实践

1. 整数溢出和下溢

Move 会在运行时检查整数溢出和下溢,如果发生会自动 abort:

module my_addr::overflow_example {
    use std::debug;
    
    #[test]
    public fun test_normal_operations() {
        // 正常的运算不会溢出
        let a: u64 = 100;
        let b: u64 = 200;
        let sum = a + b;
        debug::print(&sum); // 300
        assert!(sum == 300, 100);
        
        let product = a * b;
        debug::print(&product); // 20000
        assert!(product == 20000, 101);
    }
    
    #[test]
    #[expected_failure] // 预期这个测试会失败
    public fun test_overflow() {
        // 这会导致溢出,Move 会自动 abort
        let max_val: u64 = 18446744073709551615; // u64 最大值
        let _overflow = max_val + 1; // 溢出,程序会 abort
    }
    
    #[test]
    #[expected_failure] // 预期这个测试会失败
    public fun test_underflow() {
        // 这会导致下溢,Move 会自动 abort
        let zero: u64 = 0;
        let _underflow = zero - 1; // 下溢,程序会 abort
    }
    
    #[test]
    #[expected_failure] // 预期这个测试会失败
    public fun test_division_by_zero() {
        // 除零错误,Move 会自动 abort
        let a: u64 = 10;
        let _result = a / 0; // 除零,程序会 abort
    }
}

2. 类型选择指南

module my_addr::type_guidelines {
    // ✅ 推荐:使用 u64 作为默认整数类型
    public fun calculate_total(items: vector<u64>): u64 {
        // 实现
        0
    }
    
    // ✅ 推荐:使用 vector<u8> 表示字符串
    public fun process_name(name: vector<u8>): vector<u8> {
        // 实现
        name
    }
    
    // ✅ 推荐:使用 bool 表示状态
    public fun is_valid(value: u64): bool {
        value > 0
    }
}

小结

通过本章的学习,我们掌握了:

  1. 整数类型:u8, u16, u32, u64, u128, u256 的使用和选择
  2. 布尔类型:逻辑运算和条件判断
  3. 地址类型:区块链地址的表示和操作
  4. 向量类型:动态数组的创建和操作
  5. 字符串类型:字节向量的字符串处理
  6. 类型转换:安全和不安全的类型转换
  7. 类型推断:让编译器自动推断类型
  8. 最佳实践:避免溢出、选择合适的类型

这些基本数据类型是 Move 编程的基础,掌握它们将帮助我们更好地理解和编写 Move 程序。


下一节3.2 结构体 (Struct)

3.2 结构体 (Struct)

什么是结构体

结构体是 Move 中用于定义自定义数据类型的核心概念。它允许将多个不同类型的数据组合成一个逻辑单元,类似于其他编程语言中的类或对象。在 Move 中,结构体是构建复杂数据结构和资源的基础。

结构体字段的私有性

⚠️ 重要概念:在 Move 中,结构体的所有字段都是私有的,这意味着:

  • 结构体字段只能在定义该结构体的模块内部直接访问
  • 其他模块无法直接读取或修改结构体的字段
  • 如果需要在其他模块中访问字段,必须通过公共函数(getter/setter)来实现
  • 这种设计确保了数据封装和模块间的清晰边界

结构体的定义

基本语法

module my_addr::struct_basics {
    use std::debug;
    
    // 定义一个简单的结构体(命名字段)
    struct Person {
        name: vector<u8>,
        age: u8,
        is_student: bool,
    }
    
    // 定义一个更复杂的结构体
    struct Account {
        address: address,
        balance: u64,
        active: bool,
    }
    
    // 包含其他结构体的结构体
    struct Company {
        name: vector<u8>,
        ceo: Person,
        employees: vector<Person>,
        founded_year: u16,
    }
    
    // 🆕 元组结构体(Tuple Struct)- 新特性
    struct Point(u64, u64) has copy, drop;
    struct Color(u8, u8, u8) has copy, drop;
    struct Pair(u64, u8) has copy, drop;
    
    // 元组结构体的使用示例
    #[test]
    public fun test_tuple_structs() {
        // 创建元组结构体
        let point = Point(10, 20);
        let red_color = Color(255, 0, 0);
        let pair = Pair(42, 1);
        
        // 访问元组结构体的字段(通过索引)
        let x = point.0;  // 第一个字段
        let y = point.1;  // 第二个字段
        
        let red = red_color.0;    // R 值
        let green = red_color.1;  // G 值
        let blue = red_color.2;   // B 值
        
        let first = pair.0;   // 第一个值
        let second = pair.1;  // 第二个值
        
        debug::print(&x);     // 10
        debug::print(&y);     // 20
        debug::print(&red);   // 255
        debug::print(&first); // 42
        
        assert!(x == 10, 100);
        assert!(y == 20, 101);
        assert!(red == 255, 102);
        assert!(green == 0, 103);
        assert!(blue == 0, 104);
        assert!(first == 42, 105);
        assert!(second == 1, 106);
    }
    
    // 🆕 结构体解构(Destructuring)
    #[test]
    public fun test_struct_destructuring() {
        // 元组结构体解构
        let point = Point(100, 200);
        let Point(x, y) = point;  // 解构赋值
        
        assert!(x == 100, 107);
        assert!(y == 200, 108);
        
        let color = Color(128, 64, 32);
        let Color(r, g, b) = color;  // 解构 RGB 值
        
        assert!(r == 128, 109);
        assert!(g == 64, 110);
        assert!(b == 32, 111);
        
        let pair = Pair(999, 5);
        let Pair(number, boolean_like) = pair;  // 解构为不同变量名
        
        assert!(number == 999, 112);
        assert!(boolean_like == 5, 113);
        
        debug::print(&x);
        debug::print(&y);
        debug::print(&r);
        debug::print(&number);
    }
}

结构体的命名规范

module my_addr::naming_conventions {
    // ✅ 必须:使用 PascalCase(首字母大写)
    struct UserProfile {
        username: vector<u8>,
        email: vector<u8>,
    }
    
    struct BankAccount {
        account_number: u64,
        balance: u64,
    }
    
    // ✅ 推荐:描述性的名称
    struct TokenMetadata {
        name: vector<u8>,
        symbol: vector<u8>,
        decimals: u8,
    }
}

创建结构体实例

基本创建方式

module my_addr::struct_creation {
    use std::debug;
    
    // 命名字段结构体
    struct Point {
        x: u64,
        y: u64,
    }
    
    struct Rectangle {
        top_left: Point,
        width: u64,
        height: u64,
    }
    
    // 元组结构体
    struct TuplePoint(u64, u64) has copy, drop;
    struct RGB(u8, u8, u8) has copy, drop;
    
    #[test]
    public fun test_create_structs() {
        // 创建命名字段结构体
        let origin = Point {
            x: 0,
            y: 0,
        };
        
        let point_a = Point {
            x: 10,
            y: 20,
        };
        
        // 创建包含其他结构体的结构体
        let rect = Rectangle {
            top_left: origin,
            width: 100,
            height: 50,
        };
        
        // 🆕 创建元组结构体
        let tuple_point = TuplePoint(30, 40);
        let white_color = RGB(255, 255, 255);
        
        debug::print(&point_a.x); // 10
        debug::print(&point_a.y); // 20
        debug::print(&rect.width); // 100
        debug::print(&tuple_point.0); // 30
        debug::print(&tuple_point.1); // 40
        debug::print(&white_color.0); // 255
        
        assert!(point_a.x == 10, 100);
        assert!(point_a.y == 20, 101);
        assert!(rect.width == 100, 102);
        assert!(rect.height == 50, 103);
        assert!(tuple_point.0 == 30, 104);
        assert!(tuple_point.1 == 40, 105);
        assert!(white_color.0 == 255, 106);
        assert!(white_color.1 == 255, 107);
        assert!(white_color.2 == 255, 108);
    }
    
    // 使用构造函数创建结构体
    public fun new_point(x: u64, y: u64): Point {
        Point { x, y }
    }
    
    public fun new_rectangle(x: u64, y: u64, width: u64, height: u64): Rectangle {
        Rectangle {
            top_left: new_point(x, y),
            width,
            height,
        }
    }
    
    // 🆕 元组结构体的构造函数
    public fun new_tuple_point(x: u64, y: u64): TuplePoint {
        TuplePoint(x, y)
    }
    
    public fun new_rgb_color(r: u8, g: u8, b: u8): RGB {
        RGB(r, g, b)
    }
    
    #[test]
    public fun test_constructor_functions() {
        let point = new_point(5, 15);
        let rect = new_rectangle(0, 0, 200, 100);
        
        // 🆕 使用元组结构体构造函数
        let tuple_point = new_tuple_point(50, 60);
        let blue_color = new_rgb_color(0, 0, 255);
        
        assert!(point.x == 5, 200);
        assert!(point.y == 15, 201);
        assert!(rect.top_left.x == 0, 202);
        assert!(rect.top_left.y == 0, 203);
        assert!(rect.width == 200, 204);
        assert!(rect.height == 100, 205);
        assert!(tuple_point.0 == 50, 206);
        assert!(tuple_point.1 == 60, 207);
        assert!(blue_color.2 == 255, 208); // 蓝色分量
    }
    
    // 🆕 结构体解构的进阶用法
    #[test]
    public fun test_advanced_destructuring() {
        // 命名字段结构体解构
        let point = Point { x: 75, y: 125 };
        let Point { x, y } = point;  // 命名字段解构
        
        assert!(x == 75, 209);
        assert!(y == 125, 210);
        
        // 可以使用不同的变量名
        let Point { x: coord_x, y: coord_y } = point;
        assert!(coord_x == 75, 211);
        assert!(coord_y == 125, 212);
        
        // 元组结构体在函数参数中解构
        let tuple_point = TuplePoint(88, 99);
        let distance = calculate_distance_from_origin(tuple_point);
        assert!(distance == 187, 213); // 88 + 99 的简化距离
        
        // 在函数返回值中使用解构
        let rgb = create_custom_color();
        let RGB(red, green, blue) = rgb;
        assert!(red == 255, 214);
        assert!(green == 128, 215);
        assert!(blue == 0, 216);
        
        debug::print(&coord_x);
        debug::print(&coord_y);
        debug::print(&distance);
    }
    
    // 在函数参数中直接解构
    public fun calculate_distance_from_origin(TuplePoint(x, y): TuplePoint): u64 {
        x + y  // 简化的距离计算
    }
    
    // 返回元组结构体供解构使用
    public fun create_custom_color(): RGB {
        RGB(255, 128, 0)  // 橙色
    }
}

访问和修改结构体字段

字段访问权限

在 Move 中,结构体字段的访问遵循严格的私有性规则:

  • 模块内部:可以直接访问和修改字段
  • 模块外部:无法直接访问字段,需要通过公共函数
// 文件: my_module.move
module my_addr::my_module {
    struct Person {
        name: vector<u8>,
        age: u8,
    }
    
    // ✅ 在同一模块内可以直接访问字段
    public fun create_person(name: vector<u8>, age: u8): Person {
        Person { name, age }
    }
    
    // ✅ 提供公共访问器函数
    public fun get_name(person: &Person): vector<u8> {
        person.name
    }
    
    public fun get_age(person: &Person): u8 {
        person.age
    }
    
    // ✅ 提供公共修改器函数
    public fun set_age(person: &mut Person, new_age: u8) {
        person.age = new_age;
    }
}

// 文件: other_module.move
module my_addr::other_module {
    use my_addr::my_module::{Self, Person};
    
    public fun use_person() {
        let person = my_module::create_person(b"Alice", 25);
        
        // ❌ 编译错误!无法直接访问字段
        // let name = person.name;
        // let age = person.age;
        
        // ✅ 正确:通过公共函数访问
        let name = my_module::get_name(&person);
        let age = my_module::get_age(&person);
    }
}

字段访问

module my_addr::struct_access {
    use std::debug;
    
    struct Student {
        id: u64,
        name: vector<u8>,
        grade: u8,
        gpa: u64, // 乘以100的GPA,例如 325 表示 3.25
    }
    
    #[test]
    public fun test_field_access() {
        let student = Student {
            id: 12345,
            name: b"Alice",
            grade: 10,
            gpa: 385, // 3.85
        };
        
        // 读取字段
        let student_id = student.id;
        let student_name = student.name;
        let student_grade = student.grade;
        let student_gpa = student.gpa;
        
        debug::print(&student_id);   // 12345
        debug::print(&student_name); // b"Alice"
        debug::print(&student_grade); // 10
        debug::print(&student_gpa);  // 385
        
        assert!(student_id == 12345, 300);
        assert!(student_name == b"Alice", 301);
        assert!(student_grade == 10, 302);
        assert!(student_gpa == 385, 303);
    }
    
    #[test]
    public fun test_field_modification() {
        let mut student = Student {
            id: 12345,
            name: b"Bob",
            grade: 9,
            gpa: 350,
        };
        
        // 修改字段
        student.grade = 10;
        student.gpa = 375;
        
        assert!(student.grade == 10, 400);
        assert!(student.gpa == 375, 401);
        
        // 通过引用修改
        let grade_ref = &mut student.grade;
        *grade_ref = 11;
        
        let gpa_ref = &mut student.gpa;
        *gpa_ref = 390;
        
        assert!(student.grade == 11, 402);
        assert!(student.gpa == 390, 403);
    }
    
    // 🆕 字段访问与解构
    #[test]
    public fun test_field_access_with_destructuring() {
        let student = Student {
            id: 54321,
            name: b"Charlie",
            grade: 12,
            gpa: 395,
        };
        
        // 使用解构一次性获取多个字段
        let Student { id, name, grade, gpa } = student;
        
        assert!(id == 54321, 404);
        assert!(name == b"Charlie", 405);
        assert!(grade == 12, 406);
        assert!(gpa == 395, 407);
        
        // 部分解构,只获取需要的字段
        let Student { id: student_id, name: student_name, .. } = student;
        
        assert!(student_id == 54321, 408);
        assert!(student_name == b"Charlie", 409);
        
        // 在函数调用中使用解构
        let summary = get_student_summary(student);
        assert!(summary == b"Charlie:12", 410);
        
        debug::print(&id);
        debug::print(&name);
        debug::print(&summary);
    }
    
    // 函数参数解构示例
    public fun get_student_summary(Student { name, grade, .. }: Student): vector<u8> {
        // 这里只关心 name 和 grade,忽略其他字段
        let mut result = name;
        result.append(b":");
        // 简化的数字转字符串(实际应用中应该使用适当的转换函数)
        if (grade == 9) result.append(b"9")
        else if (grade == 10) result.append(b"10")
        else if (grade == 11) result.append(b"11")
        else if (grade == 12) result.append(b"12")
        else result.append(b"?");
        result
    }
}

结构体方法

module my_addr::struct_methods {
    use std::debug;
    
    struct Circle {
        center_x: u64,
        center_y: u64,
        radius: u64,
    }
    
    // 构造函数
    public fun new_circle(x: u64, y: u64, radius: u64): Circle {
        Circle {
            center_x: x,
            center_y: y,
            radius,
        }
    }
    
    // 获取圆的面积(简化计算,实际应该是 π * r²)
    public fun area(circle: &Circle): u64 {
        // 简化计算:3 * r * r(近似 π)
        3 * circle.radius * circle.radius
    }
    
    // 获取圆的周长(简化计算,实际应该是 2 * π * r)
    public fun circumference(circle: &Circle): u64 {
        // 简化计算:6 * r(近似 2π)
        6 * circle.radius
    }
    
    // 移动圆心
    public fun move_circle(circle: &mut Circle, new_x: u64, new_y: u64) {
        circle.center_x = new_x;
        circle.center_y = new_y;
    }
    
    // 缩放圆
    public fun scale_circle(circle: &mut Circle, scale_factor: u64) {
        circle.radius = circle.radius * scale_factor;
    }
    
    // 检查点是否在圆内(简化计算)
    public fun contains_point(circle: &Circle, x: u64, y: u64): bool {
        let dx = if (x > circle.center_x) { 
            x - circle.center_x 
        } else { 
            circle.center_x - x 
        };
        let dy = if (y > circle.center_y) { 
            y - circle.center_y 
        } else { 
            circle.center_y - y 
        };
        
        // 简化的距离计算(实际应该用勾股定理)
        dx + dy <= circle.radius
    }
    
    #[test]
    public fun test_circle_methods() {
        let mut circle = new_circle(10, 10, 5);
        
        // 测试面积计算
        let area_val = area(&circle);
        debug::print(&area_val); // 75 (3 * 5 * 5)
        assert!(area_val == 75, 500);
        
        // 测试周长计算
        let circumference_val = circumference(&circle);
        debug::print(&circumference_val); // 30 (6 * 5)
        assert!(circumference_val == 30, 501);
        
        // 测试移动
        move_circle(&mut circle, 20, 20);
        assert!(circle.center_x == 20, 502);
        assert!(circle.center_y == 20, 503);
        
        // 测试缩放
        scale_circle(&mut circle, 2);
        assert!(circle.radius == 10, 504);
        
        // 测试点包含
        let contains = contains_point(&circle, 25, 25);
        assert!(contains == true, 505);
        
        let not_contains = contains_point(&circle, 35, 35);
        assert!(not_contains == false, 506);
    }
}

结构体的复制和移动

复制语义

module my_addr::struct_copy {
    use std::debug;
    
    // 具有 copy 能力的结构体
    struct Point has copy, drop {
        x: u64,
        y: u64,
    }
    
    // 不具有 copy 能力的结构体
    struct UniquePoint has drop {
        x: u64,
        y: u64,
        id: u64,
    }
    
    #[test]
    public fun test_copy_semantics() {
        let point1 = Point { x: 10, y: 20 };
        
        // Point 有 copy 能力,可以被复制
        let point2 = point1; // 复制
        let point3 = point1; // 再次复制
        
        // 所有三个变量都可以使用
        assert!(point1.x == 10, 600);
        assert!(point2.x == 10, 601);
        assert!(point3.x == 10, 602);
        
        debug::print(&point1);
        debug::print(&point2);
        debug::print(&point3);
    }
    
    #[test]
    public fun test_move_semantics() {
        let unique_point1 = UniquePoint { x: 5, y: 15, id: 1 };
        
        // UniquePoint 没有 copy 能力,发生移动
        let unique_point2 = unique_point1; // 移动
        // unique_point1 现在不能再使用
        
        assert!(unique_point2.x == 5, 700);
        assert!(unique_point2.y == 15, 701);
        assert!(unique_point2.id == 1, 702);
        
        debug::print(&unique_point2);
        // debug::print(&unique_point1); // 错误!unique_point1 已被移动
    }
    
    public fun clone_point(point: &Point): Point {
        Point { x: point.x, y: point.y }
    }
    
    #[test]
    public fun test_explicit_clone() {
        let original = Point { x: 100, y: 200 };
        let cloned = clone_point(&original);
        
        assert!(original.x == cloned.x, 800);
        assert!(original.y == cloned.y, 801);
        
        debug::print(&original);
        debug::print(&cloned);
    }
}

嵌套结构体

复杂的嵌套结构

module my_addr::nested_structs {
    use std::debug;
    use std::vector;
    
    struct Address {
        street: vector<u8>,
        city: vector<u8>,
        country: vector<u8>,
        postal_code: vector<u8>,
    }
    
    struct Contact {
        email: vector<u8>,
        phone: vector<u8>,
    }
    
    struct Person {
        name: vector<u8>,
        age: u8,
        address: Address,
        contact: Contact,
    }
    
    struct Company {
        name: vector<u8>,
        employees: vector<Person>,
        headquarters: Address,
    }
    
    // 构造函数
    public fun new_address(
        street: vector<u8>,
        city: vector<u8>,
        country: vector<u8>,
        postal_code: vector<u8>
    ): Address {
        Address { street, city, country, postal_code }
    }
    
    public fun new_contact(email: vector<u8>, phone: vector<u8>): Contact {
        Contact { email, phone }
    }
    
    public fun new_person(
        name: vector<u8>,
        age: u8,
        address: Address,
        contact: Contact
    ): Person {
        Person { name, age, address, contact }
    }
    
    public fun new_company(
        name: vector<u8>,
        headquarters: Address
    ): Company {
        Company {
            name,
            employees: vector::empty<Person>(),
            headquarters,
        }
    }
    
    // 操作方法
    public fun add_employee(company: &mut Company, person: Person) {
        company.employees.push_back(person);
    }
    
    public fun get_employee_count(company: &Company): u64 {
        company.employees.length()
    }
    
    public fun get_company_city(company: &Company): vector<u8> {
        company.headquarters.city
    }
    
    #[test]
    public fun test_nested_structs() {
        // 创建地址
        let home_address = new_address(
            b"123 Main St",
            b"New York",
            b"USA",
            b"10001"
        );
        
        let office_address = new_address(
            b"456 Business Ave",
            b"San Francisco",
            b"USA",
            b"94105"
        );
        
        // 创建联系方式
        let contact = new_contact(
            b"alice@example.com",
            b"+1-555-0123"
        );
        
        // 创建人员
        let alice = new_person(
            b"Alice Johnson",
            30,
            home_address,
            contact
        );
        
        // 创建公司
        let mut tech_company = new_company(
            b"Tech Innovations Inc.",
            office_address
        );
        
        // 添加员工
        add_employee(&mut tech_company, alice);
        
        // 验证
        assert!(get_employee_count(&tech_company) == 1, 900);
        assert!(get_company_city(&tech_company) == b"San Francisco", 901);
        assert!(tech_company.employees[0].name == b"Alice Johnson", 902);
        assert!(tech_company.employees[0].age == 30, 903);
        assert!(tech_company.employees[0].address.city == b"New York", 904);
        
        debug::print(&tech_company.name);
        debug::print(&get_employee_count(&tech_company));
    }
}

结构体的能力 (Abilities)

四种基本能力

module my_addr::struct_abilities {
    use std::debug;
    
    // 1. copy: 可以被复制
    struct Copyable has copy, drop {
        value: u64,
    }
    
    // 2. drop: 可以被丢弃(销毁)
    struct Droppable has drop {
        data: vector<u8>,
    }
    
    // 3. store: 可以被存储在全局存储中
    struct Storable has store {
        content: vector<u8>,
    }
    
    // 4. key: 可以作为全局存储的键
    struct Resource has key {
        id: u64,
        data: vector<u8>,
    }
    
    // 组合能力
    struct FlexibleStruct has copy, drop, store {
        name: vector<u8>,
        value: u64,
    }
    
    struct CompleteStruct has copy, drop, store, key {
        id: u64,
        name: vector<u8>,
        data: vector<u8>,
    }
    
    #[test]
    public fun test_copyable() {
        let original = Copyable { value: 42 };
        let copy1 = original; // 复制
        let copy2 = original; // 再次复制
        
        assert!(original.value == 42, 1000);
        assert!(copy1.value == 42, 1001);
        assert!(copy2.value == 42, 1002);
        
        debug::print(&original);
        debug::print(&copy1);
        debug::print(&copy2);
    }
    
    #[test]
    public fun test_droppable() {
        let droppable = Droppable { data: b"Hello" };
        assert!(droppable.data == b"Hello", 1100);
        // droppable 在函数结束时自动被丢弃
    }
    
    public fun create_flexible(): FlexibleStruct {
        FlexibleStruct {
            name: b"Flexible",
            value: 100,
        }
    }
    
    #[test]
    public fun test_flexible() {
        let flex1 = create_flexible();
        let flex2 = flex1; // copy
        let flex3 = flex1; // copy again
        
        assert!(flex1.name == b"Flexible", 1200);
        assert!(flex2.name == b"Flexible", 1201);
        assert!(flex3.name == b"Flexible", 1202);
        assert!(flex1.value == 100, 1203);
        assert!(flex2.value == 100, 1204);
        assert!(flex3.value == 100, 1205);
    }
}

泛型结构体

参数化结构体

module my_addr::generic_structs {
    use std::debug;
    use std::vector;
    use std::option::{Self, Option};
    
    // 命名字段泛型结构体
    struct Box<T> has copy, drop, store {
        value: T,
    }
    
    struct Pair<T, U> has copy, drop, store {
        first: T,
        second: U,
    }
    
    struct Container<T> has drop, store {
        items: vector<T>,
    }
    
    // 🆕 元组结构体泛型
    struct TuplePair<T, U>(T, U) has copy, drop, store;
    struct Triple<T, U, V>(T, U, V) has copy, drop, store;
    struct Wrapper<T>(T) has copy, drop, store;
    
    // 约束泛型参数
    struct Comparable<T: copy + drop> has copy, drop, store {
        value: T,
    }
    
    // 构造函数
    public fun new_box<T>(value: T): Box<T> {
        Box { value }
    }
    
    public fun new_pair<T, U>(first: T, second: U): Pair<T, U> {
        Pair { first, second }
    }
    
    public fun new_container<T>(): Container<T> {
        Container { items: vector::empty<T>() }
    }
    
    // 🆕 元组结构体构造函数
    public fun new_tuple_pair<T, U>(first: T, second: U): TuplePair<T, U> {
        TuplePair(first, second)
    }
    
    public fun new_triple<T, U, V>(first: T, second: U, third: V): Triple<T, U, V> {
        Triple(first, second, third)
    }
    
    public fun new_wrapper<T>(value: T): Wrapper<T> {
        Wrapper(value)
    }
    
    // 操作方法
    public fun get_box_value<T>(box: &Box<T>): &T {
        &box.value
    }
    
    public fun set_box_value<T>(box: &mut Box<T>, value: T) {
        box.value = value;
    }
    
    public fun get_first<T, U>(pair: &Pair<T, U>): &T {
        &pair.first
    }
    
    public fun get_second<T, U>(pair: &Pair<T, U>): &U {
        &pair.second
    }
    
    // 🆕 元组结构体访问方法
    public fun get_tuple_first<T, U>(pair: &TuplePair<T, U>): &T {
        &pair.0
    }
    
    public fun get_tuple_second<T, U>(pair: &TuplePair<T, U>): &U {
        &pair.1
    }
    
    public fun get_wrapper_value<T>(wrapper: &Wrapper<T>): &T {
        &wrapper.0
    }
    
    public fun add_item<T>(container: &mut Container<T>, item: T) {
        container.items.push_back(item);
    }
    
    public fun get_item_count<T>(container: &Container<T>): u64 {
        container.items.length()
    }
    
    public fun get_item<T>(container: &Container<T>, index: u64): &T {
        &container.items[index]
    }
    
    #[test]
    public fun test_generic_box() {
        // 数字盒子
        let mut number_box = new_box<u64>(42);
        assert!(*get_box_value(&number_box) == 42, 1300);
        
        set_box_value(&mut number_box, 100);
        assert!(*get_box_value(&number_box) == 100, 1301);
        
        // 字符串盒子
        let string_box = new_box<vector<u8>>(b"Hello");
        assert!(*get_box_value(&string_box) == b"Hello", 1302);
        
        // 布尔盒子
        let bool_box = new_box<bool>(true);
        assert!(*get_box_value(&bool_box) == true, 1303);
        
        debug::print(get_box_value(&number_box));
        debug::print(get_box_value(&string_box));
        debug::print(get_box_value(&bool_box));
    }
    
    #[test]
    public fun test_generic_pair() {
        let pair = new_pair<u64, vector<u8>>(123, b"ABC");
        
        assert!(*get_first(&pair) == 123, 1400);
        assert!(*get_second(&pair) == b"ABC", 1401);
        
        let bool_pair = new_pair<bool, bool>(true, false);
        assert!(*get_first(&bool_pair) == true, 1402);
        assert!(*get_second(&bool_pair) == false, 1403);
        
        debug::print(get_first(&pair));
        debug::print(get_second(&pair));
    }
    
    #[test]
    public fun test_generic_container() {
        let mut number_container = new_container<u64>();
        
        add_item(&mut number_container, 10);
        add_item(&mut number_container, 20);
        add_item(&mut number_container, 30);
        
        assert!(get_item_count(&number_container) == 3, 1500);
        assert!(*get_item(&number_container, 0) == 10, 1501);
        assert!(*get_item(&number_container, 1) == 20, 1502);
        assert!(*get_item(&number_container, 2) == 30, 1503);
        
        let mut string_container = new_container<vector<u8>>();
        add_item(&mut string_container, b"First");
        add_item(&mut string_container, b"Second");
        
        assert!(get_item_count(&string_container) == 2, 1504);
        assert!(*get_item(&string_container, 0) == b"First", 1505);
        assert!(*get_item(&string_container, 1) == b"Second", 1506);
        
        debug::print(&get_item_count(&number_container));
        debug::print(&get_item_count(&string_container));
    }
    
    // 🆕 测试元组结构体泛型
    #[test]
    public fun test_tuple_generics() {
        // 元组对
        let tuple_pair = new_tuple_pair<u64, vector<u8>>(456, b"XYZ");
        assert!(*get_tuple_first(&tuple_pair) == 456, 1600);
        assert!(*get_tuple_second(&tuple_pair) == b"XYZ", 1601);
        
        // 三元组
        let triple = new_triple<bool, u64, vector<u8>>(true, 789, b"Triple");
        assert!(triple.0 == true, 1602);
        assert!(triple.1 == 789, 1603);
        assert!(triple.2 == b"Triple", 1604);
        
        // 包装器
        let wrapper = new_wrapper<u64>(999);
        assert!(*get_wrapper_value(&wrapper) == 999, 1605);
        
        debug::print(get_tuple_first(&tuple_pair));
        debug::print(&triple.1);
        debug::print(get_wrapper_value(&wrapper));
    }
    
    // 🆕 泛型结构体解构
    #[test]
    public fun test_generic_destructuring() {
        // 泛型元组结构体解构
        let tuple_pair = new_tuple_pair<u64, bool>(777, true);
        let TuplePair(number, flag) = tuple_pair;
        
        assert!(number == 777, 1606);
        assert!(flag == true, 1607);
        
        let triple = new_triple<u8, u16, u32>(255, 65535, 4294967295);
        let Triple(small, medium, large) = triple;
        
        assert!(small == 255, 1608);
        assert!(medium == 65535, 1609);
        assert!(large == 4294967295, 1610);
        
        let wrapper = new_wrapper<vector<u8>>(b"Wrapped");
        let Wrapper(content) = wrapper;
        
        assert!(content == b"Wrapped", 1611);
        
        // 泛型命名字段结构体解构
        let box_value = new_box<u64>(888);
        let Box { value } = box_value;
        
        assert!(value == 888, 1612);
        
        let pair_value = new_pair<bool, vector<u8>>(false, b"Test");
        let Pair { first, second } = pair_value;
        
        assert!(first == false, 1613);
        assert!(second == b"Test", 1614);
        
        debug::print(&number);
        debug::print(&content);
        debug::print(&value);
    }
    
    // 在泛型函数中使用解构
    public fun extract_first<T, U>(TuplePair(first, _): TuplePair<T, U>): T {
        first
    }
    
    public fun extract_wrapper_content<T>(Wrapper(content): Wrapper<T>): T {
        content
    }
    
    #[test]
    public fun test_generic_destructuring_functions() {
        let pair = new_tuple_pair<u64, vector<u8>>(123, b"ABC");
        let first_value = extract_first(pair);
        assert!(first_value == 123, 1615);
        
        let wrapper = new_wrapper<bool>(true);
        let wrapped_value = extract_wrapper_content(wrapper);
        assert!(wrapped_value == true, 1616);
        
        debug::print(&first_value);
        debug::print(&wrapped_value);
    }
}

实际应用示例

Token 元数据结构

module my_addr::token_example {
    use std::debug;
    use std::string::{Self, String};
    use std::option::{Self, Option};
    
    struct TokenMetadata has copy, drop, store {
        name: String,
        symbol: String,
        decimals: u8,
        icon_url: Option<String>,
        project_url: Option<String>,
    }
    
    struct TokenInfo has key {
        metadata: TokenMetadata,
        total_supply: u64,
        max_supply: Option<u64>,
    }
    
    struct Account has key {
        balance: u64,
    }
    
    // 构造函数
    public fun create_token_metadata(
        name: vector<u8>,
        symbol: vector<u8>,
        decimals: u8,
        icon_url: Option<vector<u8>>,
        project_url: Option<vector<u8>>
    ): TokenMetadata {
        TokenMetadata {
            name: string::utf8(name),
            symbol: string::utf8(symbol),
            decimals,
            icon_url: option::map(icon_url, |url| string::utf8(url)),
            project_url: option::map(project_url, |url| string::utf8(url)),
        }
    }
    
    // 获取器方法
    public fun get_name(metadata: &TokenMetadata): String {
        metadata.name
    }
    
    public fun get_symbol(metadata: &TokenMetadata): String {
        metadata.symbol
    }
    
    public fun get_decimals(metadata: &TokenMetadata): u8 {
        metadata.decimals
    }
    
    public fun get_icon_url(metadata: &TokenMetadata): Option<String> {
        metadata.icon_url
    }
    
    public fun get_project_url(metadata: &TokenMetadata): Option<String> {
        metadata.project_url
    }
    
    #[test]
    public fun test_token_metadata() {
        let metadata = create_token_metadata(
            b"My Token",
            b"MTK",
            8,
            option::some(b"https://example.com/icon.png"),
            option::some(b"https://myproject.com")
        );
        
        assert!(get_name(&metadata) == string::utf8(b"My Token"), 2000);
        assert!(get_symbol(&metadata) == string::utf8(b"MTK"), 2001);
        assert!(get_decimals(&metadata) == 8, 2002);
        assert!(option::is_some(&get_icon_url(&metadata)), 2003);
        assert!(option::is_some(&get_project_url(&metadata)), 2004);
        
        debug::print(&metadata.name);
        debug::print(&metadata.symbol);
        debug::print(&metadata.decimals);
    }
}

最佳实践和常见错误

1. 结构体设计原则

module my_addr::best_practices {
    use std::vector;
    
    // ✅ 好的设计:字段相关且内聚
    struct User {
        id: u64,
        username: vector<u8>,
        email: vector<u8>,
        created_at: u64,
    }
    
    // ✅ 提供访问器函数(getter)
    public fun get_user_id(user: &User): u64 {
        user.id
    }
    
    public fun get_username(user: &User): vector<u8> {
        user.username
    }
    
    public fun get_email(user: &User): vector<u8> {
        user.email
    }
    
    // ✅ 提供修改器函数(setter)
    public fun update_email(user: &mut User, new_email: vector<u8>) {
        // 可以在这里添加验证逻辑
        assert!(new_email.length() > 0, 1);
        user.email = new_email;
    }
    
    // ✅ 好的设计:明确的职责
    struct BankAccount {
        account_number: u64,
        balance: u64,
        owner: address,
    }
    
    // ✅ 封装业务逻辑
    public fun deposit(account: &mut BankAccount, amount: u64) {
        assert!(amount > 0, 2);
        account.balance = account.balance + amount;
    }
    
    public fun withdraw(account: &mut BankAccount, amount: u64) {
        assert!(amount > 0, 3);
        assert!(account.balance >= amount, 4);
        account.balance = account.balance - amount;
    }
    
    public fun get_balance(account: &BankAccount): u64 {
        account.balance
    }
    
    // ❌ 不好的设计:字段不相关
    struct MixedData {
        user_name: vector<u8>,
        account_balance: u64,
        random_number: u64,
        is_weekend: bool,
    }
    
    // ✅ 好的设计:使用合适的能力
    struct Config has copy, drop, store {
        max_users: u64,
        fee_rate: u64,
    }
    
    // ✅ 好的设计:资源类型
    struct Coin has key {
        value: u64,
    }
    
    // 🆕 解构的最佳实践
    
    // ✅ 推荐:使用解构简化代码
    public fun process_user_data(user: User): (u64, vector<u8>) {
        let User { id, username, .. } = user;  // 只解构需要的字段
        (id, username)
    }
    
    // ✅ 推荐:在函数参数中直接解构
    public fun validate_bank_account(BankAccount { balance, owner, .. }: &BankAccount): bool {
        *balance > 0 && *owner != @0x0
    }
    
    // ✅ 推荐:使用解构进行模式匹配
    public fun categorize_config(config: Config): vector<u8> {
        let Config { max_users, fee_rate } = config;
        if (max_users > 1000 && fee_rate < 100) {
            b"Enterprise"
        } else if (max_users > 100) {
            b"Professional"
        } else {
            b"Basic"
        }
    }
    
    // ❌ 避免:过度解构,影响可读性
    /*
    public fun bad_destructuring(User { id: user_identification_number, username: user_display_name, email: user_contact_email, created_at: user_registration_timestamp }: User) {
        // 变量名过长,影响可读性
    }
    */
    
    // ✅ 推荐:简洁的变量名
    public fun good_destructuring(User { id, username, email, created_at }: User) {
        // 清晰简洁
    }
}

2. 构造函数模式

module my_addr::constructor_patterns {
    use std::vector;
    
    struct Student {
        id: u64,
        name: vector<u8>,
        scores: vector<u64>,
    }
    
    // ✅ 基本构造函数
    public fun new_student(id: u64, name: vector<u8>): Student {
        Student {
            id,
            name,
            scores: vector::empty<u64>(),
        }
    }
    
    // ✅ 带验证的构造函数
    public fun new_student_validated(id: u64, name: vector<u8>): Student {
        assert!(id > 0, 100);
        assert!(name.length() > 0, 101);
        
        Student {
            id,
            name,
            scores: vector::empty<u64>(),
        }
    }
    
    // ✅ 构建器模式
    public fun new_student_builder(): StudentBuilder {
        StudentBuilder {
            id: 0,
            name: vector::empty<u8>(),
            scores: vector::empty<u64>(),
        }
    }
    
    struct StudentBuilder {
        id: u64,
        name: vector<u8>,
        scores: vector<u64>,
    }
    
    public fun with_id(builder: StudentBuilder, id: u64): StudentBuilder {
        StudentBuilder { id, ..builder }
    }
    
    public fun with_name(builder: StudentBuilder, name: vector<u8>): StudentBuilder {
        StudentBuilder { name, ..builder }
    }
    
    public fun build(builder: StudentBuilder): Student {
        assert!(builder.id > 0, 200);
        assert!(builder.name.length() > 0, 201);
        
        Student {
            id: builder.id,
            name: builder.name,
            scores: builder.scores,
        }
    }
}

3. 常见错误和解决方案

module my_addr::common_mistakes {
    struct Data has drop {
        values: vector<u64>,
    }
    
    // ❌ 错误:直接修改不可变引用
    /*
    public fun wrong_modify(data: &Data) {
        data.values.push_back(10); // 编译错误!
    }
    */
    
    // ✅ 正确:使用可变引用
    public fun correct_modify(data: &mut Data, value: u64) {
        data.values.push_back(value);
    }
    
    // ❌ 错误:返回局部变量的引用
    /*
    public fun wrong_return(): &Data {
        let data = Data { values: vector::empty<u64>() };
        &data // 编译错误!
    }
    */
    
    // ✅ 正确:返回拥有所有权的值
    public fun correct_return(): Data {
        Data { values: vector::empty<u64>() }
    }
    
    // ✅ 正确:接受引用参数并返回计算结果
    public fun get_sum(data: &Data): u64 {
        let mut sum = 0;
        let mut i = 0;
        while (i < data.values.length()) {
            sum = sum + data.values[i];
            i = i + 1;
        };
        sum
    }
}

// 演示跨模块访问错误
module my_addr::field_access_errors {
    struct Person has drop {
        name: vector<u8>,
        age: u8,
    }
    
    public fun create_person(name: vector<u8>, age: u8): Person {
        Person { name, age }
    }
    
    // ✅ 正确:提供公共访问函数
    public fun get_name(person: &Person): vector<u8> {
        person.name
    }
    
    public fun get_age(person: &Person): u8 {
        person.age
    }
}

module my_addr::user_module {
    use my_addr::field_access_errors::{Self, Person};
    
    public fun use_person_correctly() {
        let person = field_access_errors::create_person(b"Alice", 25);
        
        // ❌ 错误:尝试直接访问其他模块的结构体字段
        /*
        let name = person.name;  // 编译错误!
        let age = person.age;    // 编译错误!
        */
        
        // ✅ 正确:通过公共函数访问
        let name = field_access_errors::get_name(&person);
        let age = field_access_errors::get_age(&person);
    }
}

小结

通过本章的学习,我们掌握了:

  1. 结构体定义:如何定义和命名结构体(命名字段和元组结构体)
  2. 实例创建:不同的创建和初始化方式
  3. 字段操作:访问和修改结构体字段(包括索引访问)
  4. 🆕 结构体解构:使用模式匹配解构结构体,简化代码
  5. 方法定义:为结构体定义相关的操作函数
  6. 复制和移动:理解值语义和引用语义
  7. 嵌套结构体:构建复杂的数据结构
  8. 能力系统:copy、drop、store、key 四种能力
  9. 泛型结构体:参数化的结构体定义(命名字段和元组形式)
  10. 实际应用:Token 元数据等实际场景
  11. 最佳实践:设计原则和常见错误避免

结构体特性对比

特性命名字段结构体元组结构体
定义语法struct Point { x: u64, y: u64 }struct Point(u64, u64)
创建语法Point { x: 10, y: 20 }Point(10, 20)
字段访问point.x, point.ypoint.0, point.1
解构语法let Point { x, y } = pointlet Point(x, y) = point
部分解构let Point { x, .. } = point不支持部分解构
函数参数解构fn(Point { x, y }: Point)fn(Point(x, y): Point)
适用场景复杂数据结构,字段含义明确简单数据组合,临时数据结构
可读性高(字段名称清晰)中等(需要记住字段顺序)

解构的使用场景

函数参数解构 - 直接在参数中解构,简化函数体
模式匹配 - 根据结构体内容进行条件判断
批量赋值 - 一次性提取多个字段值
部分解构 - 只提取需要的字段,忽略其他字段
泛型解构 - 在泛型函数中使用解构提高代码复用性

结构体是 Move 中构建复杂数据类型的基础,掌握它们将帮助我们更好地设计和实现 Move 程序的数据模型。


上一节3.1 基本数据类型
下一节3.3 引用和借用

结构体最佳实践

函数

本章介绍 Move 语言中的函数相关内容。

资源

本章介绍 Move 语言中的资源类型。

泛型与能力

本章介绍 Move 语言中的泛型和能力(Abilities)。

测试

本章介绍 Move 语言的测试相关内容。

9.1 枚举类型(Enum)

Move 的枚举实现

Move 没有原生 enum,但可以用 sum type(联合体)模式实现。

枚举的定义与使用

module my_addr::enum_example {
    struct MyEnum has copy, drop, store {
        tag: u8, // 0: A, 1: B
        value_a: u64,
        value_b: bool,
    }
    // ...示例代码...
}

枚举的常见用法

  • 状态机
  • 错误码
  • 事件类型

小结

枚举模式让 Move 代码更具表达力。

10.1 跨合约导入与调用

Move 跨模块/合约调用基础

导入其他模块

module my_addr::import_example {
    use 0x1::coin;
    // ...示例代码...
}

跨模块调用函数

module my_addr::call_example {
    use 0x1::coin;
    public fun call_coin(): u64 {
        coin::balance_of(@0x1)
    }
}

跨合约调用的注意事项

  • 访问控制
  • 能力(abilities)
  • 依赖管理

小结

跨合约调用是 Move 合约协作的基础。

示例

本章收录了 Move 语言的实用示例代码。

Hello Blockchain

Simple Token