iT邦幫忙

2022 iThome 鐵人賽

DAY 5
0
Web 3

Smart-Contract Language: Move系列 第 9

Day 9 Struct

  • 分享至 

  • xImage
  •  

Struct 是包含複雜數據(或無數據)的自定義類型。它可以描述為一個簡單的 key value 存儲,其中key 是屬性的名稱,value 是存儲的內容。使用關鍵字定義struct。結構最多可以有 4 個能力,它們是用類型定義指定的。

比較特別是,如果結構值無法複製且無法刪除,我們通常將其稱為*資源。*在這種情況下,資源值必須在函數結束時轉移所有權,默認情況下,結構是線性的和短暫的。意思是它們:不能被複製,不能被刪除,不能被存儲在全局存儲中。這意味著所有值都必須轉移所有權(線性),並且必須在程序執行結束時處理這些值(臨時)。我們可以通過賦予結構能力來放鬆這種行爲(後面會介紹),這些能力允許值被複製或刪除,也可以存儲在全局存儲中或定義全局存儲模式。

未轉移所有權,造成 error
https://ithelp.ithome.com.tw/upload/images/20220920/20150953CRpH8pDeJO.png

在 Move 中,要創建自定義類型的唯一辦法是使用 Struct

schema

struct NAME {
    FIELD1: TYPE1,
    FIELD2: TYPE2,
    ...
}

// 給予 struct 能力
// 擁有四種能力可以使用
// **copy**
// Allows values of types with this ability to be copied.

// **drop**
// Allows values of types with this ability to be popped/dropped.

// **store**
// Allows values of types with this ability to exist inside a struct in global storage.

// **key**
// Allows the type to serve as a key for global storage operations.

struct NAME2 has copy, drop { x: TYPE1, y: TYPE2 }

example

module M {

    // struct can be without fields
    // but it is a new type
    struct Empty {}

    struct MyStruct {
        field1: address,
        field2: bool,
        field3: Empty
    }

    struct Example {
        field1: u8,
        field2: address,
        field3: u64,
        field4: bool,
        field5: bool,

        // you can use another struct as type
        field6: MyStruct
    }
}

每一個定義個 struct 都會變成新的類型,可以透過該 Module 進行訪問,就像訪問 Module 裡的 function 一樣。

M::MyStruct;
// or
M::Example;

遞迴結構定義將會錯誤。

struct Foo { x: Foo }
//              ^ error! Foo cannot contain Foo

使用新結構

要使用定義的結構,需要創建他的實例,兩者必須在同一個 Module 裡。

module Country {
    struct Country {
        id: u8,
        population: u64
    }

    // Contry is a return type of this function!
    public fun new_country(c_id: u8, c_population: u64): Country {
        // structure creation is an expression
        let country = Country {
            id: c_id,
            population: c_population
        };

        country
    }
}

可以透過解構的方式,直接創建實例。

public fun new_country(id: u8, population: u64): Country {
    // id matches id: u8 field
    // population matches population field
    Country {
        id,
        population
    }

    // or even in one line: Country { id, population }
}

Move 允許創建空的結構。

public fun empty(): Empty {
    Empty {}
}

訪問結構內容

前面說到,宣告和使用 struct 必須要在同一個 Module, 使用 .(dot) 來進行訪問。\

如果同個 Module 內有兩個 Struct, 我們也可以通過其中一個訪問到另一個 Struct

schema

<struct>.<field>
<struct>.<field>.<nested_struct_field> // and field can be another struct so

example

// ...
public fun get_country_population(country: Country): u64 {
    country.population // <struct>.<property>
}

解析結構

要拿到 Struct 裡的東西,除了可以直接使用 dot 之外,還可以透過解析 (破壞) 的方式來拿到。

address 0x3 {
module M {
    struct Foo has drop { x: u64, y: u64 }

    public fun new_foo(): Foo {
        Foo { x: 42, y: 10 }
    }
}
}

schema

let <STRUCT DEF> = <STRUCT>

example

module Country {

    // ...

    // we'll return values of this struct outside
    public fun destroy(country: Country): (u8, u64) {

        // variables must match struct fields
        // all struct fields must be specified
        let Country { id, population } = country;

        // after destruction country is dropped
        // but its fields are now variables and
        // can be used
        (id, population)
    }
}

注意,Move 靜止宣告為使用的變量,當解析的 Struct 有不使用的資料時,我們可以這樣寫

module Country {
    // ...

    public fun destroy(country: Country) {

        // this way you destroy struct and don't create unused variables
        let Country { id: _, population: _ } = country;

        // or take only id and don't init `population` variable
        // let Country { id, population: _ } = country;
    }
}

使用 Getter 函數

前面說到, Struct 都只能在同一個 Module 裡宣告和使用,如果想要外部可讀,我們需要透過 getter 將 Struct 作為返回值進行傳遞。

example

address 0x3 {
	module M { 
	    struct Foo has drop { x: u64, y: u64 } // 給予 Foo Struct drop 能力

	    public fun new_foo(): Foo {
	        Foo { x: 42, y: 10 }
	    }

			// getter
		public fun x(foo: &Foo): u64 {
            foo.x
        }
	}
}

script {
    use 0x1::Debug;
    use 0x3::M;

    fun main() {
        
        let res = M::new_foo();
      
        Debug::print(&res); // print {42, 10}

				Debug::print(&res.x); // 會 error, 必須使用 getter 拿到

				Debug::print(&M::x(&res)); // print 42

    }
    
}

本篇詳細介紹 Struct 以及如何使用,開頭題到的四種能力,之後會更詳細說明,只要記得在 Move 默認情況下,Struct 是線性的和短暫的。同時不能被複製,不能被刪除,不能被存儲在全局存儲中。

在 Module 外想要拿到 Struct 則需要透過 getter 來傳遞個別回傳值,我們 Move to Day10


上一篇
Day 8 Tuple and Unit
下一篇
Day 10 Types with Abilities
系列文
Smart-Contract Language: Move30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言