下一代的智能合约开发语言Move(七)

前言

上一篇文章我们主要介绍了Move语言中的泛型,了解了泛型的定义与使用,在Move中泛型可以用于结构体和函数。结构体这个数据类型能够帮助我们创建自己的类型和存储复杂的数据结构,但是有时候我们需要更灵活,扩展性更好和管理性更好的数据结构,为了满足这个需求,Move提供了Vector。

什么是Vector

Vector是Move内置的数据类型用于存储大量的数据,这是用于任意类型数据的集合的解决方法,它的能力是虚拟机提供的,所以使用它的唯一方式就是标准库,例子如下:

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);
    }
}

vector可以存储数据的最大容量为18446744073709551615,我们还可以对其进行包装,方便使用:

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)
    }
}


内联 Vector 定义的十六进制数组和字符串

Vector 也可以表示字符串。VM支持将vector作为参数传递给main脚本中的函数。也可以使用十六进制字面值(literal)在脚本或模块中定义vector

script {

    use 0x1::Vector;

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

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

更简单的方法是使用字符串字面值(literal):

script {

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

它们被视为 ASCII 字符串,也被解释为 vector。

vector速查表

这是标准库中 Vector 方法的简短列表:

创建一个类型为的空向量

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

获取向量的长度

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

将元素 e 添加到向量末尾

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

获取对向量元素的可变引用。不可变引用可使用Vector::borrow()

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

从向量的末尾取出一个元素

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

Resource

接下来我们就要学习Move中非常重要的一个概念Resource,它使 Move 变得独一无二,安全且强大。
首先,让我们看一下 Diem 开发者网站上的关于 Resource 的要点(将 Libra 改名为 Diem 后,原页面已删除):

Move 的主要功能是提供了自定义 Resource 类型。Resource 类型为安全的数字资产编码且提供了丰富的可编程性。 Resource 在Move语言中就是普通的值。它们可以作为数据结构被存储,作为参数被传递给函数,也可以从函数中返回。

Resource 是一种特殊的结构体,可以在 Move 代码中定义和创建,也可以使用现有的 Resource。因此,我们可以像使用任何其它数据(比如向量或结构体)那样来管理数字资产。

Move 类型系统为 Resource 提供了特殊的安全保证。Resource 永远不能被复制,重用或丢弃。Resource 类型只能由定义该类型的模块创建或销毁。这些检查由 Move 虚拟机通过字节码校验强制执行。Move 虚拟机将拒绝运行任何尚未通过字节码校验的代码。

signer

在开始使用 Resource 之前,我们需要了解 signer 类型以及这种类型存在的原因。

Signer 是一种原生的类似 Resource 的不可复制的类型,它包含了交易发送者的地址。
Signer 类型代表了发送者权限。换句话说,使用 signer 意味着可以访问发送者的地址和 Resource。它与signature没有直接关系,就Move VM 而言,它仅表示发送者。
Signer 只有一种 ability: Drop。

脚本的signer

Signer 是原生类型,使用前必须先创建。与 vector 这样的原生类型不同,signer 不能直接在代码中创建,但是可以作为脚本参数传递:

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

Signer 参数无需手动将其传递到脚本中,客户端(CLI)会自动将它放入你的脚本中。而且,signer 自始至终都只是引用,虽然标准库中可以访问签名者的实际值,但使用此值的函数是私有的,无法在其他任何地方使用或传递 signer 值。

当前,约定俗成的 signer 类型的变量名是 account

标准库中的Signer模块

原生类型离不开原生方法, signer 的原生方法包含在0x1::Signer模块中。这个模块相对比较简单,具体可以参考 Diem 标准库 Signer 模块的实现:

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)
    }
}

模块中包含两个方法,一个是原生方法,另一个是 Move 方法。后者使用更方便,因为它使用了取值运算符来复制地址。

模块中的signer

module M {
    use 0x1::Signer;

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

使用&signer作为函数的参数说明该函数正在使用发送者的地址。
引入signer类型的原因之一是要明确显示哪些函数需要发送者权限,哪些不需要。因此,函数不能欺骗用户未经授权访问其 Resource。

最后

这篇文章主要介绍了Move语言中的vector和Resource的一些相关基本概念,更多文章可以关注公众号QStack。

猜你喜欢

转载自blog.csdn.net/QStack/article/details/128944342