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 的应用场景

🪙 代币合约

#![allow(unused)]
fn main() {
// 简单的代币合约示例
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:版本控制

安装步骤

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` 文件:

```rust
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. 模块声明

#![allow(unused)]
fn main() {
module hello_blockchain::message {
}
  • module:关键字,声明这是一个模块
  • hello_blockchain::message:模块的完整名称
    • hello_blockchain:地址(在 Move.toml 中定义)
    • message:模块名称

2. 导入标准库

#![allow(unused)]
fn main() {
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. 资源定义

#![allow(unused)]
fn main() {
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. 常量定义

#![allow(unused)]
fn main() {
const ENO_MESSAGE: u64 = 0;
}
  • const:声明一个常量
  • ENO_MESSAGE:表示没有消息的错误代码

5. 函数定义

#![allow(unused)]
fn main() {
#[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. 函数实现

#![allow(unused)]
fn main() {
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:获取并返回消息内容
#![allow(unused)]
fn main() {
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. 测试函数

#![allow(unused)]
fn main() {
#[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. 注释

#![allow(unused)]
fn main() {
// 单行注释
/// 文档注释
/* 多行注释 */
}

2. 变量声明

#![allow(unused)]
fn main() {
let x = 10;           // 类型推断
let y: u64 = 20;      // 显式类型声明
}

3. 条件语句

#![allow(unused)]
fn main() {
if (condition) {
    // 代码块
} else {
    // 代码块
}
}

4. 循环

#![allow(unused)]
fn main() {
while (condition) {
    // 循环体
}
}

5. 函数调用

#![allow(unused)]
fn main() {
module_name::function_name(arg1, arg2);
}

6. 断言

#![allow(unused)]
fn main() {
assert!(condition, error_code);
}

常见错误和调试

调试技巧

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

小结

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

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

下一节2.1 Module 概念与结构

2.1 Module 概念与结构

什么是 Module?

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

Module 的核心概念

1. 命名空间

#![allow(unused)]
fn main() {
module my_addr::math {
    // 模块内容
}
}
  • my_addr:地址(Address),类似于包名
  • math:模块名
  • my_addr::math:完整的模块标识符

2. 封装性

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

3. 可重用性

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

Module 的基本结构

完整的模块示例

#![allow(unused)]
fn main() {
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. 模块声明

#![allow(unused)]
fn main() {
module <address>::<module_name> {
    // 模块内容
}
}

2. 导入语句

#![allow(unused)]
fn main() {
use std::signer;      // 导入标准库模块
use my_addr::math;    // 导入自定义模块
}

3. 常量定义

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

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

#![allow(unused)]
fn main() {
const ERROR_CODE: u64 = 1;
const MAX_BALANCE: u64 = 1000000;
}

4. 结构体定义

#![allow(unused)]
fn main() {
struct MyStruct has key, store {
    field1: u64,
    field2: vector<u8>,
}
}

5. 函数定义

#![allow(unused)]
fn main() {
public fun public_function() {
    // 公共函数
}

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

Module 的访问控制

访问修饰符

1. public

#![allow(unused)]
fn main() {
public fun public_function() {
    // 可以被其他模块调用
}
}

2. 默认(私有)

#![allow(unused)]
fn main() {
fun private_function() {
    // 只能在模块内部调用
}
}

3. public(friend)

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

#![allow(unused)]
fn main() {
public(friend) fun friend_function() {
    // 只能被友元模块调用
}
}

4. public(package)

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

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

访问控制示例

#![allow(unused)]
fn main() {
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. 单一职责原则

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

#![allow(unused)]
fn main() {
// ✅ 好的设计
module my_addr::math {
    // 数学运算相关
}

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

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

2. 高内聚,低耦合

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

#![allow(unused)]
fn main() {
// 高内聚的模块设计
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 语句导入其他模块

#![allow(unused)]
fn main() {
// 基础模块
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. 工具模块模式

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

#![allow(unused)]
fn main() {
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. 服务模块模式

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

#![allow(unused)]
fn main() {
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. 命名规范

#![allow(unused)]
fn main() {
// ✅ 好的命名
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 插件可以读取模块中的文档注释

#![allow(unused)]
fn main() {
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 的方式

#![allow(unused)]
fn main() {
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 提供了多种无符号整数类型,用于表示不同范围的整数值:

#![allow(unused)]
fn main() {
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极大数值密码学、精确计算

布尔类型

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

#![allow(unused)]
fn main() {
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
    }
}
}

地址类型

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

#![allow(unused)]
fn main() {
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 中的动态数组:

#![allow(unused)]
fn main() {
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 中,字符串实际上是字节向量:

#![allow(unused)]
fn main() {
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 是强类型语言,但支持一些隐式和显式类型转换:

#![allow(unused)]
fn main() {
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 支持类型推断,编译器可以自动推断变量类型:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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. 类型选择指南

#![allow(unused)]
fn main() {
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)来实现
  • 这种设计确保了数据封装和模块间的清晰边界

结构体的定义

基本语法

#![allow(unused)]
fn main() {
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);
    }
}
}

结构体的命名规范

#![allow(unused)]
fn main() {
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,
    }
}
}

创建结构体实例

基本创建方式

#![allow(unused)]
fn main() {
module my_addr::struct_creation {
    use std::debug;
    
    // 命名字段结构体
    // 为了方便演示,为 struct 增加 copy 和 drop 的能力
    struct Point has copy, drop{
        x: u64,
        y: u64,
    }
    
    // 为了方便演示,为 struct 增加 copy 和 drop 的能力
    struct Rectangle has copy, drop {
        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 RGB(red, green, blue) = create_custom_color();
        assert!(red == 255, 214);
        assert!(green == 128, 215);
        assert!(blue == 0, 216);
        
        debug::print(&coord_x);
        debug::print(&coord_y);
    }
    
    // 返回元组结构体供解构使用
    public fun create_custom_color(): RGB {
        RGB(255, 128, 0)  // 橙色
    }
}
}

访问和修改结构体字段

字段访问权限

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

  • 模块内部:可以直接访问和修改字段
  • 模块外部:无法直接访问字段,需要通过公共函数
#![allow(unused)]
fn main() {
// 文件: my_module.move
module my_addr::my_module {

    // 为了方便演示,为 struct 增加 drop 的能力
    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
    }
    
    // ✅ 提供公共修改器函数
    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;
    
    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);
    }
}
}

字段访问

#![allow(unused)]
fn main() {
module my_addr::struct_access {
    use std::debug;
    
    // 为了方便演示,为 struct 增加 copy 和 drop 的能力
    struct Student has copy, drop{
        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 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);

        debug::print(&id);
        debug::print(&name);
    }
    
}
}

结构体方法

在为结构体编写代码时,可以使用 self 作为参数名,使得编译器可以自动识别当前函数为结构体方法。 并可以使用 . 语法,使用函数 circle.move_circle(20, 20);

否则只能写为 move_circle(&mut circle, 20, 20);

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

结构体的复制和移动

复制语义

#![allow(unused)]
fn main() {
module my_addr::struct_copy {
    #[test_only]
    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);
    }
}
}

嵌套结构体

复杂的嵌套结构

#![allow(unused)]
fn main() {
module my_addr::nested_structs {
    #[test_only]
    use std::debug;
    use std::vector;
    
    // 为了方便演示,为 struct 增加 drop 的能力
    struct Address has drop {
        street: vector<u8>,
        city: vector<u8>,
        country: vector<u8>,
        postal_code: vector<u8>,
    }
    
    // 为了方便演示,为 struct 增加 drop 的能力
    struct Contact has drop {
        email: vector<u8>,
        phone: vector<u8>,
    }
    
    // 为了方便演示,为 struct 增加 drop 的能力
    struct Person has drop {
        name: vector<u8>,
        age: u8,
        address: Address,
        contact: Contact,
    }
    
    // 为了方便演示,为 struct 增加 drop 的能力
    struct Company has drop {
        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(self: &mut Company, person: Person) {
        self.employees.push_back(person);
    }
    
    public fun get_employee_count(self: &Company): u64 {
        self.employees.length()
    }
    
    public fun get_company_city(self: &Company): vector<u8> {
        self.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 tech_company = new_company(
            b"Tech Innovations Inc.",
            office_address
        );
        
        // 添加员工
        tech_company.add_employee(alice);
        
        // 验证
        assert!(tech_company.get_employee_count() == 1, 900);
        assert!(tech_company.get_company_city() == 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(&tech_company.get_employee_count());
    }
}
}

结构体的能力 (Abilities)

四种基本能力

#![allow(unused)]
fn main() {
module my_addr::struct_abilities {
    #[test_only]
    use std::debug;
    
    // 1. copy: 可以被复制
    struct Copyable has copy {
        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);

        // 由于 Copyable 结构体未实现了 drop 能力,所以不能进行丢弃
        // 需要手动解构结构体
        // 可以将结构体中的值赋值给变量
        let Copyable { value: _value } = original;
        // 也可以使用 _ 忽略某个值
        let Copyable { value: _ } = copy1;
        // 也可以使用 .. 忽略所有值
        let Copyable { .. } = 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);
    }
}
}

泛型结构体

参数化结构体

#![allow(unused)]
fn main() {
module my_addr::generic_structs {
    #[test_only]
    use std::debug;
    use std::vector;
    
    // ========================================
    // 泛型结构体定义
    // ========================================
    
    // 1. 命名字段泛型结构体 - Box<T>
    // 这是一个通用的容器结构体,可以存储任意类型的值
    // 添加了 copy, drop, store 能力以便于演示和测试
    struct Box<T> has copy, drop, store {
        value: T,  // 存储任意类型的值
    }
    
    // 2. 双类型泛型结构体 - Pair<T, U>
    // 可以存储两个不同类型的值,类似于元组但使用命名字段
    struct Pair<T, U> has copy, drop, store {
        first: T,   // 第一个值
        second: U,  // 第二个值
    }
    
    // 3. 容器泛型结构体 - Container<T>
    // 一个可以存储多个同类型元素的容器
    struct Container<T> has drop, store {
        items: vector<T>,  // 使用 vector 存储多个元素
    }
    
    // 4. 元组结构体泛型 - 使用位置索引访问字段
    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;  // 单值包装器
    
    // 5. 带约束的泛型结构体 - Comparable<T>
    // 泛型参数 T 必须具有 copy 和 drop 能力
    struct Comparable<T: copy + drop> has copy, drop, store {
        value: T,
    }
    
    // ========================================
    // 构造函数
    // ========================================
    
    // 创建 Box 实例
    public fun new_box<T>(value: T): Box<T> {
        Box { value }
    }
    
    // 创建 Pair 实例
    public fun new_pair<T, U>(first: T, second: U): Pair<T, U> {
        Pair { first, second }
    }
    
    // 创建空的 Container 实例
    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)
    }
    
    // ========================================
    // 访问和操作方法
    // ========================================
    
    // Box 相关操作
    public fun get_box_value<T>(self: &Box<T>): &T {
        &self.value  // 返回对内部值的引用
    }
    
    public fun set_box_value<T: drop>(self: &mut Box<T>, value: T) {
        self.value = value;  // 设置新的值
    }
    
    // Pair 相关操作
    public fun get_first<T, U>(self: &Pair<T, U>): &T {
        &self.first  // 获取第一个值
    }
    
    public fun get_second<T, U>(self: &Pair<T, U>): &U {
        &self.second  // 获取第二个值
    }
    
    // 元组结构体访问方法 - 使用位置索引
    public fun get_tuple_first<T, U>(self: &TuplePair<T, U>): &T {
        &self.0  // 访问第一个元素(索引 0)
    }
    
    public fun get_tuple_second<T, U>(self: &TuplePair<T, U>): &U {
        &self.1  // 访问第二个元素(索引 1)
    }
    
    public fun get_wrapper_value<T>(self: &Wrapper<T>): &T {
        &self.0  // 访问包装的值
    }
    
    // Container 相关操作
    public fun add_item<T>(self: &mut Container<T>, item: T) {
        self.items.push_back(item);  // 向容器添加元素
    }
    
    public fun get_item_count<T>(self: &Container<T>): u64 {
        self.items.length()  // 获取容器中元素的数量
    }
    
    public fun get_item<T>(self: &Container<T>, index: u64): &T {
        &self.items[index]  // 根据索引获取元素
    }
    
    // ========================================
    // 测试函数
    // ========================================
    
    #[test]
    public fun test_generic_box() {
        // 测试不同类型的 Box 结构体
        
        // 1. 数字盒子 - 存储 u64 类型
        let number_box = new_box<u64>(42);
        assert!(*number_box.get_box_value() == 42, 1300);
        
        // 修改盒子中的值
        number_box.set_box_value(100);
        assert!(*number_box.get_box_value() == 100, 1301);
        
        // 2. 字符串盒子 - 存储 vector<u8> 类型
        let string_box = new_box<vector<u8>>(b"Hello");
        assert!(*string_box.get_box_value() == b"Hello", 1302);
        
        // 3. 布尔盒子 - 存储 bool 类型
        let bool_box = new_box<bool>(true);
        assert!(*bool_box.get_box_value() == true, 1303);
        
        // 打印所有盒子的值进行验证
        debug::print(number_box.get_box_value());
        debug::print(string_box.get_box_value());
        debug::print(bool_box.get_box_value());
    }
    
    #[test]
    public fun test_generic_pair() {
        // 测试 Pair 结构体 - 可以存储两个不同类型的值
        
        // 创建包含数字和字符串的对
        let pair = new_pair<u64, vector<u8>>(123, b"ABC");
        
        // 验证第一个和第二个值
        assert!(*pair.get_first() == 123, 1400);
        assert!(*pair.get_second() == b"ABC", 1401);
        
        // 创建包含两个布尔值的对
        let bool_pair = new_pair<bool, bool>(true, false);
        assert!(*bool_pair.get_first() == true, 1402);
        assert!(*bool_pair.get_second() == false, 1403);
        
        // 打印值进行验证
        debug::print(pair.get_first());
        debug::print(pair.get_second());
    }
    
    #[test]
    public fun test_generic_container() {
        // 测试 Container 结构体 - 可以存储多个同类型的元素
        
        // 1. 数字容器
        let number_container = new_container<u64>();
        
        // 向容器添加多个数字
        number_container.add_item(10);
        number_container.add_item(20);
        number_container.add_item(30);
        
        // 验证容器的大小和内容
        assert!(number_container.get_item_count() == 3, 1500);
        assert!(*number_container.get_item(0) == 10, 1501);
        assert!(*number_container.get_item(1) == 20, 1502);
        assert!(*number_container.get_item(2) == 30, 1503);
        
        // 2. 字符串容器
        let string_container = new_container<vector<u8>>();
        string_container.add_item(b"First");
        string_container.add_item(b"Second");
        
        // 验证字符串容器
        assert!(string_container.get_item_count() == 2, 1504);
        assert!(*string_container.get_item(0) == b"First", 1505);
        assert!(*string_container.get_item(1) == b"Second", 1506);
        
        // 打印容器大小进行验证
        debug::print(&number_container.get_item_count());
        debug::print(&string_container.get_item_count());
    }
    
    #[test]
    public fun test_tuple_generics() {
        // 测试元组结构体泛型 - 使用位置索引访问字段
        
        // 1. 元组对 - 使用位置索引访问
        let tuple_pair = new_tuple_pair<u64, vector<u8>>(456, b"XYZ");
        assert!(*tuple_pair.get_tuple_first() == 456, 1600);
        assert!(*tuple_pair.get_tuple_second() == b"XYZ", 1601);
        
        // 2. 三元组 - 直接使用位置索引
        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); // 第三个元素
        
        // 3. 包装器 - 包装单个值
        let wrapper = new_wrapper<u64>(999);
        assert!(*wrapper.get_wrapper_value() == 999, 1605);
        
        // 打印值进行验证
        debug::print(tuple_pair.get_tuple_first());
        debug::print(&triple.1);
        debug::print(wrapper.get_wrapper_value());
    }
    
    #[test]
    public fun test_generic_destructuring() {
        // 测试泛型结构体的解构 - 将结构体分解为单独的变量
        
        // 1. 元组结构体解构
        let tuple_pair = new_tuple_pair<u64, bool>(777, true);
        let TuplePair(number, flag) = tuple_pair;  // 解构为 number 和 flag
        
        assert!(number == 777, 1606);
        assert!(flag == true, 1607);
        
        // 2. 三元组解构
        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);
        
        // 3. 包装器解构
        let wrapper = new_wrapper<vector<u8>>(b"Wrapped");
        let Wrapper(content) = wrapper;  // 解构为 content
        
        assert!(content == b"Wrapped", 1611);
        
        // 4. 命名字段结构体解构
        let box_value = new_box<u64>(888);
        let Box { value } = box_value;  // 使用字段名解构
        
        assert!(value == 888, 1612);
        
        // 5. Pair 结构体解构
        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);
    }

    #[test]
    public fun test_generic_destructuring_functions() {
        // 测试使用函数访问元组结构体的值
        
        // 使用函数获取元组对的第一个值
        let pair = new_tuple_pair<u64, vector<u8>>(123, b"ABC");
        let first_value = pair.get_tuple_first();
        assert!(*first_value == 123, 1615);
        
        // 使用函数获取包装器的值
        let wrapper = new_wrapper<bool>(true);
        let wrapped_value = wrapper.get_wrapper_value();
        assert!(*wrapped_value == true, 1616);
        
        // 打印获取的值进行验证
        debug::print(first_value);
        debug::print(wrapped_value);
    }
}
}

实际应用示例

Token 元数据结构

看了一大堆基础,可以来看看模拟一个 Token Metadata 是如何记录的吧

#![allow(unused)]
fn main() {
module my_addr::token_example {
    #[test_only]
    use std::debug;
    use std::string::{Self, String};
    use std::option::Option;
    
    // ========================================
    // 代币相关结构体定义
    // ========================================
    
    // 代币元数据结构体 - 存储代币的基本信息
    // 具有 copy, drop, store 能力,可以在函数间传递和存储
    struct TokenMetadata has copy, drop, store {
        name: String,           // 代币名称,如 "Bitcoin"
        symbol: String,         // 代币符号,如 "BTC"
        decimals: u8,           // 小数位数,如 8 表示支持 8 位小数
        icon_url: Option<String>,      // 代币图标 URL(可选)
        project_url: Option<String>,   // 项目官网 URL(可选)
    }
    
    // 代币信息结构体 - 存储代币的完整信息
    // 具有 key 能力,可以作为全局存储的资源
    struct TokenInfo has key {
        metadata: TokenMetadata,    // 代币元数据
        total_supply: u64,          // 当前总供应量
        max_supply: Option<u64>,    // 最大供应量(可选,None 表示无上限)
    }
    
    // 账户结构体 - 存储用户账户信息
    // 具有 key 能力,可以作为全局存储的资源,但是不能带有 copy 能力,防止被复制
    struct Account has key {
        balance: u64,  // 账户余额
    }
    
    // ========================================
    // 构造函数
    // ========================================
    
    // 创建代币元数据
    // 参数使用 vector<u8> 类型,然后转换为 String 类型
    public fun create_token_metadata(
        name: vector<u8>,           // 代币名称的字节数组
        symbol: vector<u8>,         // 代币符号的字节数组
        decimals: u8,               // 小数位数
        icon_url: Option<vector<u8>>,      // 图标 URL 的字节数组(可选)
        project_url: Option<vector<u8>>    // 项目 URL 的字节数组(可选)
    ): TokenMetadata {
        TokenMetadata {
            name: string::utf8(name),      // 将字节数组转换为 UTF-8 字符串
            symbol: string::utf8(symbol),  // 将字节数组转换为 UTF-8 字符串
            decimals,
            // 使用 map 函数处理可选值:如果有值则转换为字符串,否则保持 None
            icon_url: icon_url.map(|url| string::utf8(url)),
            project_url: project_url.map(|url| string::utf8(url)),
        }
    }
    
    // ========================================
    // 获取器方法(Getter Methods)
    // ========================================
    
    // 获取代币名称
    public fun get_name(self: &TokenMetadata): String {
        self.name
    }
    
    // 获取代币符号
    public fun get_symbol(self: &TokenMetadata): String {
        self.symbol
    }
    
    // 获取代币小数位数
    public fun get_decimals(self: &TokenMetadata): u8 {
        self.decimals
    }
    
    // 获取代币图标 URL(可能为 None)
    public fun get_icon_url(self: &TokenMetadata): Option<String> {
        self.icon_url
    }
    
    // 获取项目官网 URL(可能为 None)
    public fun get_project_url(self: &TokenMetadata): Option<String> {
        self.project_url
    }
    
    // ========================================
    // 测试函数
    // ========================================
    
    #[test]
    public fun test_token_metadata() {
        // 创建一个代币元数据实例进行测试
        
        // 创建包含所有字段的代币元数据
        let metadata = create_token_metadata(
            b"My Token",                                    // 代币名称
            b"MTK",                                        // 代币符号
            8,                                             // 8 位小数
            option::some(b"https://example.com/icon.png"), // 图标 URL
            option::some(b"https://myproject.com")         // 项目 URL
        );
        
        // 验证所有字段的值是否正确
        assert!(metadata.get_name() == string::utf8(b"My Token"), 2000);
        assert!(metadata.get_symbol() == string::utf8(b"MTK"), 2001);
        assert!(metadata.get_decimals() == 8, 2002);
        assert!(metadata.get_icon_url().is_some(), 2003);      // 验证图标 URL 存在
        assert!(metadata.get_project_url().is_some(), 2004);   // 验证项目 URL 存在
        
        // 打印元数据信息进行验证
        debug::print(&metadata.name);
        debug::print(&metadata.symbol);
        debug::print(&metadata.decimals);
    }
}
}

小结

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

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

结构体特性对比

特性命名字段结构体元组结构体
定义语法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不支持部分解构
适用场景复杂数据结构,字段含义明确简单数据组合,临时数据结构
可读性高(字段名称清晰)中等(需要记住字段顺序)

解构的使用场景

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

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


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

3.3 引用和可变引用

引用概述

在 Move 语言中,引用(References)是一种强大的工具,允许我们在不获取值的所有权的情况下访问和操作数据。引用提供了安全的内存访问机制,防止悬空指针和数据竞争。

在前面的教程中,你也应该也看到了 &&mut 的用法,这就是不可变引用和可变引用

🔗 引用的类型

Move 语言支持两种类型的引用:

  1. 不可变引用 (&T) - 只读访问
  2. 可变引用 (&mut T) - 读写访问

不可变引用 (&T)

不可变引用允许你只读访问数据,不能修改被引用的值。

基本语法

#![allow(unused)]
fn main() {
let ref = &value;           // 创建不可变引用
let field_ref = &obj.field; // 字段引用
}

示例代码

#![allow(unused)]
fn main() {
#[test]
public fun test_immutable_references() {
    let number = 42;
    let number_ref = &number;  // 创建不可变引用
    
    // 通过解引用访问值
    assert!(*number_ref == 42, 100);
    
    // number_ref 是只读的,不能修改
    // *number_ref = 50; // 这会导致编译错误
}
}

可变引用 (&mut T)

可变引用允许你读取和修改被引用的数据。

基本语法

#![allow(unused)]
fn main() {
let mut_ref = &mut value;           // 创建可变引用
let field_mut_ref = &mut obj.field; // 字段可变引用
}

示例代码

#![allow(unused)]
fn main() {
#[test]
public fun test_mutable_references() {
    let number = 42;
    let number_mut_ref = &mut number;  // 创建可变引用
    
    // 读取值
    assert!(*number_mut_ref == 42, 200);
    
    // 修改值
    *number_mut_ref = 100;
    assert!(*number_mut_ref == 100, 201);
}
}

结构体字段引用

可以创建指向结构体特定字段的引用。

字段不可变引用

#![allow(unused)]
fn main() {
public fun get_name(person: &Person): &String {
    &person.name  // 返回字段的不可变引用
}
}

字段可变引用

#![allow(unused)]
fn main() {
public fun update_age(person: &mut Person, new_age: u8) {
    person.age = new_age;  // 直接修改字段
    // 或使用引用
    let age_ref = &mut person.age;
    *age_ref = new_age;
}
}

引用规则和限制

1. 借用规则

Move 遵循严格的借用规则,确保内存安全:

  • 规则1:在任何给定时间,对于任何值,要么有一个可变引用,要么有多个不可变引用
  • 规则2:引用必须始终有效(不能悬空)

2. 生命周期

#![allow(unused)]
fn main() {
public fun reference_lifetime_example() {
    let number = 42;
    let ref1 = &number;      // 创建不可变引用
    let ref2 = &number;      // 可以有多个不可变引用
    
    // let mut_ref = &mut number; // 错误!不能同时有可变和不可变引用
    
    assert!(*ref1 == 42, 300);
    assert!(*ref2 == 42, 301);
}
}

3. 可变性传播

#![allow(unused)]
fn main() {
struct SomeStruct has key, store {
    field: u64,
}

public fun mutability_propagation(data: &mut SomeStruct, new_value: u64) {
    let field_ref = &mut data.field;  // 继承可变性
    *field_ref = new_value;
}
}

函数参数中的引用

传递引用

#![allow(unused)]
fn main() {
// 接受不可变引用
public fun read_only_function(data: &SomeStruct): u64 {
    data.some_field
}

// 接受可变引用
public fun modify_function(data: &mut SomeStruct) {
    data.some_field = 100;
}
}

返回引用

#![allow(unused)]
fn main() {
// 返回不可变引用
public fun get_field_ref(data: &SomeStruct): &u64 {
    &data.some_field
}

// 返回可变引用
public fun get_field_mut_ref(data: &mut SomeStruct): &mut u64 {
    &mut data.some_field
}
}

引用和所有权

移动 vs 借用

#![allow(unused)]
fn main() {
public fun ownership_vs_borrowing() {
    let data = SomeStruct { field: 42 };
    
    // 移动所有权
    let owned = data;  // data 不再可用
    
    // 借用(创建引用)
    let data2 = SomeStruct { field: 100 };
    let borrowed = &data2;  // data2 仍然可用
    
    // 可以继续使用 data2
    let field_value = data2.field;
}
}

借用检查器

Move 的借用检查器在编译时验证所有引用使用的安全性:

#![allow(unused)]
fn main() {
public fun borrow_checker_example() {
    let value = 42;
    let ref1 = &value;
    
    // 这是安全的
    let ref2 = &value;
    assert!(*ref1 == *ref2, 400);
    
    // 创建可变引用会结束所有不可变引用的生命周期
    let mut_ref = &mut value;
    *mut_ref = 100;
    
    // ref1 和 ref2 在这里不再有效
}
}

实际应用示例

1. 数据验证

#![allow(unused)]
fn main() {
public fun validate_person(person: &Person): bool {
    person.age > 0 && !person.name.is_empty()
}
}

2. 条件修改

#![allow(unused)]
fn main() {
public fun conditional_update(person: &mut Person, condition: bool) {
    if (condition) {
        person.age = person.age + 1;
    }
}
}

3. 嵌套结构体访问

#![allow(unused)]
fn main() {
public fun access_nested_field(company: &Company): &String {
    &company.ceo.person.name
}

public fun update_nested_field(company: &mut Company, new_name: String) {
    company.ceo.person.name = new_name;
}
}

常见模式

1. 可选字段处理

#![allow(unused)]
fn main() {
public fun process_optional_field(data: &SomeStruct) {
    if (data.optional_field.is_some()) {
        let value = & data.optional_field;
        // 处理值
    }
}
}

2. 引用链

#![allow(unused)]
fn main() {
public fun reference_chain(data: &mut ComplexStruct) {
    let level1 = &mut data.level1;
    let level2 = &mut level1.level2;
    let level3 = &mut level2.level3;
    
    *level3 = new_value;
}
}

3. 临时引用

#![allow(unused)]
fn main() {
public fun temporary_reference(data: &mut SomeStruct) {
    {
        let temp_ref = &mut data.field;
        *temp_ref = compute_new_value();
    }  // temp_ref 的生命周期在这里结束
    
    // 可以创建新的引用
    let another_ref = &data.field;
}
}

性能考虑

引用的优势

  1. 节省 Gas - 引用在运行时开销较少,gas 较低
  2. 避免复制 - 大型数据结构不需要复制

高级主题

1. 引用类型推断

#![allow(unused)]
fn main() {
public fun type_inference_example() {
    let data = SomeStruct { field: 42 };
    let ref_data = &data;  // 类型自动推断为 &SomeStruct
    
    // 显式类型注解
    let explicit_ref: &SomeStruct = &data;
}
}

2. 泛型引用

#![allow(unused)]
fn main() {
public fun generic_reference<T>(data: &T): &T {
    data  // 返回相同的引用
}

public fun generic_mutable_reference<T>(data: &mut T) {
    // 对泛型可变引用的操作
}
}

3. 引用作为结构体字段

注意:Move 不允许将引用存储在结构体中,但可以作为函数参数和返回值使用。

#![allow(unused)]
fn main() {
// 这是不允许的
// struct BadStruct {
//     ref_field: &u64,  // 编译错误
// }

// 正确的做法是使用函数参数
public fun work_with_reference(data: &SomeStruct) {
    // 在函数作用域内使用引用
}
}

总结

引用是 Move 语言中的核心概念,提供了安全、高效的内存访问机制。理解引用的规则和最佳实践对于编写安全、高性能的 Move 代码至关重要。

关键要点

  1. 安全性:引用系统防止悬空指针和数据竞争
  2. 节省 Gas:无需复制值,节省 Gas
  3. 灵活性:支持复杂的数据访问模式
  4. 编译时检查:所有引用安全性在编译时验证

下一步

在下一节中,我们将学习 Move 语言中的函数定义和调用,以及如何在函数中有效使用引用。


相关链接

函数

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

资源

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

泛型与能力

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

测试

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

9.1 枚举类型(Enum)

Move 的枚举实现

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

枚举的定义与使用

#![allow(unused)]
fn main() {
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 跨模块/合约调用基础

导入其他模块

#![allow(unused)]
fn main() {
module my_addr::import_example {
    use 0x1::coin;
    // ...示例代码...
}
}

跨模块调用函数

#![allow(unused)]
fn main() {
module my_addr::call_example {
    use 0x1::coin;
    public fun call_coin(): u64 {
        coin::balance_of(@0x1)
    }
}
}

跨合约调用的注意事项

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

小结

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

示例

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

Hello Blockchain

Simple Token