Skip to main content

類型反射 (Type Reflection)

在程式語言中,反射 (Reflection) 是指程式檢查並修改自身結構和行為的能力。Move 支援有限形式的反射,讓您可以在執行時期檢查值的類型。當您需要在同質集合 (homogeneous collection) 中儲存類型資訊,或者想檢查某個類型是否來自特定的套件時,這非常有用。

類型反射實作在 標準庫 模組 std::type_name 中。它提供了一組函式,其中最主要的是 with_defining_idswith_original_ids

let defining_type_name: TypeName = type_name::with_defining_ids<T>();
let original_type_name: TypeName = type_name::with_original_ids<T>();

// 僅返回套件的 "ID"。
let defining_package: address = type_name::defining_id<T>();
let original_package: address = type_name::original_id<T>();

定義 ID (Defining IDs) vs. 原始 ID (Original IDs)

瞭解 定義 ID原始 ID 之間的區別非常重要。

  • 原始 ID 是套件第一次發佈時的 ID(在第一次升級之前)。
  • 定義 ID 是引入該反射類型的套件 ID,當在套件升級中引入新類型時,此屬性變得至關重要。

例如,假設一個套件的第一個版本發佈在 0xA 並引入了 Version1 類型。隨後,在一次升級中,該套件移動到了地址 0xB 並引入了一個新類型 Version2。對於 Version1,定義 ID 和原始 ID 是相同的。然而,對於 Version2,它們則不同:原始 ID 是 0xA,而定義 ID 是 0xB

// 注意:值 `0xA` 和 `0xB` 僅用於說明目的!
// 請勿嘗試執行此程式碼,因為它必然會失敗。
module book::upgrade;

// 在初始版本中引入。
// 定義 ID:0xA
// 原始 ID:0xA
//
// 使用定義 ID:0xA::upgrade::Version1
// 使用原始 ID:0xA::upgrade::Version1
public struct Version1 has drop {}

// 在套件升級中引入。
// 定義 ID:0xB
// 原始 ID:0xA
//
// 使用定義 ID:0xB::upgrade::Version2
// 使用原始 ID:0xA::upgrade::Version2
public struct Version2 has drop {}

實務應用

這個模組非常直觀,對結果允許的操作僅限於獲取字串表示形式,以及提取該類期的模組和地址。

module book::type_reflection;

use std::ascii::String;
use std::type_name::{Self, TypeName};

/// 回傳型別 `T` 的名稱及其模組和地址的函式。
public fun do_i_know_you<T>(): (String, String, String) {
let type_name: TypeName = type_name::with_defining_ids<T>();

// 有一種借用的方式
let str: &String = type_name.as_string();

let module_name: String = type_name.module_string();
let address_str: String = type_name.address_string();

// 和一種消耗值的方式
let str = type_name.into_string();

(str, module_name, address_str)
}

#[test_only]
public struct MyType {}

#[test_only]
use std::unit_test::assert_eq;

#[test]
fun test_type_reflection() {
let (type_name, module_name, _address_str) = do_i_know_you<MyType>();

assert_eq!(module_name, b"type_reflection".to_ascii_string());
}

延伸閱讀