The next generation of smart contract development language Move (7)

foreword

In the previous article, we mainly introduced the generics in the Move language, and learned about the definition and use of generics. In Move, generics can be used for structures and functions. The data type structure can help us create our own types and store complex data structures, but sometimes we need more flexible, scalable and manageable data structures. To meet this demand, Move provides Vector.

What is Vector

Vector is a built-in data type of Move for storing large amounts of data. This is a solution for collections of any type of data. Its capabilities are provided by the virtual machine, so the only way to use it is the standard library. Examples are as follows:

script {
    use 0x1::Vector;

    fun main() {
        // 使用泛型创建一个空的vector
        let a = Vector::empty<u8>();
        let i = 0;

        // 填充数据
        while (i < 10) {
            Vector::push_back(&mut a, i);
            i = i + 1;
        };

        // 打印vector的长度
        let a_len = Vector::length(&a);
        0x1::Debug::print<u64>(&a_len);

        // 从vector中移除数据
        Vector::pop_back(&mut a);
        Vector::pop_back(&mut a);

        // 重新打印长度
        let a_len = Vector::length(&a);
        0x1::Debug::print<u64>(&a_len);
    }
}

The maximum capacity that vector can store data is 18446744073709551615, and we can also pack it for easy use:

module Shelf {

    use 0x1::Vector;

    struct Box<T> {
        value: T
    }

    struct Shelf<T> {
        boxes: vector<Box<T>>
    }

    public fun create_box<T>(value: T): Box<T> {
        Box { value }
    }

    public fun value<T: copy>(box: &Box<T>): T {
        *&box.value
    }

    public fun create<T>(): Shelf<T> {
        Shelf {
            boxes: Vector::empty<Box<T>>()
        }
    }

    public fun put<T>(shelf: &mut Shelf<T>, box: Box<T>) {
        Vector::push_back<Box<T>>(&mut shelf.boxes, box);
    }

    public fun remove<T>(shelf: &mut Shelf<T>): Box<T> {
        Vector::pop_back<Box<T>>(&mut shelf.boxes)
    }

    public fun size<T>(shelf: &Shelf<T>): u64 {
        Vector::length<Box<T>>(&shelf.boxes)
    }
}


Hex arrays and strings defined by inline Vector

Vectors can also represent strings. The VM supports passing vectors as arguments to functions in the main script. Vectors can also be defined in scripts or modules using hexadecimal literals

script {

    use 0x1::Vector;

    // 这时main函数接受参数的方式
    fun main(name: vector<u8>) {
        let _ = name;

        
        // 这是 "hello world" 字符串!
        let str = x"68656c6c6f20776f726c64";
    }
}

An easier way is to use a string literal:

script {

    fun main() {
        let _ = b"hello world";
    }
}

They are treated as ASCII strings and also interpreted as vectors.

vector cheat sheet

Here's a short list of Vector methods in the standard library:

Creates an empty vector of type

Vector::empty<E>(): vector<E>;

get the length of the vector

Vector::length<E>(v: &vector<E>): u64;

add the element e to the end of the vector

Vector::push_back<E>(v: &mut vector<E>, e: E);

Get a mutable reference to the elements of the vector. Immutable references can use Vector::borrow()

Vector::borrow_mut<E>(v: &mut vector<E>, i: u64): &E;

take an element from the end of the vector

Vector::pop_back<E>(v: &mut vector<E>): E;

Resource

Next we will learn a very important concept Resource in Move, which makes Move unique, safe and powerful.
First, let's take a look at the key points about Resource on the Diem developer website (the original page has been deleted after renaming Libra to Diem):

The main function of Move is to provide a custom Resource type. Resource types encode secure digital assets and provide rich programmability. A Resource is an ordinary value in the Move language. They can be stored as data structures, passed as arguments to functions, and returned from functions.

Resource is a special structure that can be defined and created in Move code, or an existing Resource can be used. Therefore, we can manage digital assets like any other data such as vectors or structures.

The Move type system provides special safety guarantees for Resource . Resource can never be copied, reused or discarded. A Resource type can only be created or destroyed by the module that defines it. These checks are enforced by the Move virtual machine through bytecode verification. The Move VM will refuse to run any code that has not passed bytecode verification.

signer

Before we start working with Resources, we need to understand the signer type and why it exists.

Signer is a native Resource-like non-copyable type that contains the address of the sender of the transaction.
The Signer type represents the authority of the sender. In other words, using a signer means having access to the sender's address and Resource. It is not directly related to the signature, as far as the Move VM is concerned, it just means the sender.
Signer has only one ability: Drop.

script signer

Signer is a native type and must be created before use. Unlike primitive types like vector, signer cannot be created directly in code, but can be passed as a script parameter:

script {
    fun main(account: signer) {
        let _ = account;
    }
}

Signer parameter No need to manually pass it into the script, the client (CLI) will automatically put it into your script. Also, signer is just a reference throughout, and while the actual value of signer is accessible in the standard library, the function that uses this value is private and cannot use or pass the signer value anywhere else.

Currently, the customary variable name of the signer type is account

The Signer module in the standard library

Native types are inseparable from native methods, and the native methods of signer are included in the 0x1::Signer module. This module is relatively simple. For details, please refer to the implementation of the Signer module in the Diem standard library:

module Signer {
    // Borrows the address of the signer
    // Conceptually, you can think of the `signer`
    // as being a resource struct wrapper arround an address
    // ```
    // resource struct Signer { addr: address }
    // ```
    // `borrow_address` borrows this inner field
    native public fun borrow_address(s: &signer): &address;

    // Copies the address of the signer
    public fun address_of(s: &signer): address {
        *borrow_address(s)
    }
}

The module contains two methods, one is a native method and the other is a Move method. The latter is more convenient to use because it uses the access operator to copy the address.

signer in the module

module M {
    use 0x1::Signer;

    // let's proxy Signer::address_of
    public fun get_address(account: signer): address {
        Signer::address_of(&account)
    }
}

Using &signer as an argument to a function indicates that the function is using the sender's address.
One of the reasons for introducing the signer type is to make it clear which functions require sender permissions and which do not. Therefore, a function cannot trick a user into gaining unauthorized access to its resource.

at last

This article mainly introduces some basic concepts related to vector and Resource in the Move language. For more articles, you can pay attention to the official account QStack.

Guess you like

Origin blog.csdn.net/QStack/article/details/128944342