前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >4.MOVE从入门到实战-可编程Resource-如何使用Resource

4.MOVE从入门到实战-可编程Resource-如何使用Resource

作者头像
Tiny熊
发布2022-11-07 11:11:18
5080
发布2022-11-07 11:11:18
举报

本文作者:木头[1]

定义

Resource[2] 是一种用 keystore ability 限制了的结构体:

代码语言:javascript
复制
module M {
    struct T has key, store {
        field: u64
    }
}

Resource 的限制

在代码中,Resource 类型有几个主要限制:

  1. Resource 存储在帐户下。因此,只有在分配帐户后才会存在,并且只能通过该帐户访问。
  2. 一个帐户同一时刻只能容纳一个某类型的 Resource。
  3. Resource 不能被复制;与它对应的是一种特殊的kindresource,它与copyable不同。
  4. Resource 必需被使用,这意味着必须将新创建的 Resource move 到某个帐户下,从帐户移出的 Resource 必须被解构或存储在另一个帐户下。

创建和移动 Resource

首先,让我们创建模块:

代码语言:javascript
复制
// sources/Collection.move
module std::Collection {

    struct Item has store {
        // we'll think of the properties later
    }

    struct Collection has key {
        items: vector<Item>
    }
}

一个模块里最主要的 Resource 通常跟模块取相同的名称(例如这里的 Collection)。遵循这个惯例,你的模块将易于阅读和使用。

创建和移动

我们定义了一个 Resource 结构体T,该结构体将保存一个向量,向量里面存放Item类型的元素。现在,让我们看看如何创建新集合以及如何在 account 下存储 Resource。Resource 将永久保存在发送者的地址下,没有人可以从所有者那里修改或取走此 Resource。

代码语言:javascript
复制
// sources/Collection.move
module std::Collection {
    use std::vector;

    struct Item has store {}

    struct Collection has key {
        items: vector<Item>
    }

    /// note that &signer type is passed here!
    public fun start_collection(account: &signer) {
        move_to<Collection>(account, Collection {
            items: vector::empty<Item>()
        })
    }
}

还记得 signer 吗?现在,你将了解它的运作方式!移动 Resource 到 account 需要使用内建函数 move_to,需要 signer 作为第一个参数,T 作为第二个参数。move_to 函数的签名可以表示为:

代码语言:javascript
复制
native fun move_to<T: key>(account: &signer, value: T);

总结一下上面所学的内容:

  1. 你只能将 Resource 放在自己的帐户下。你无权访问另一个帐户的 signer 值,因此无法放置 Resource 到其它账户。
  2. 一个地址下最多只能存储一个同一类型的 Resource。两次执行相同的操作是不行的,比如第二次尝试创建已有 Resource 将会导致失败。

查看 Resource 是否存在

Move 提供 exists函数来查看某 Resource 是否存在于给定地址下,函数签名如下:

代码语言:javascript
复制
native fun exists<T: key>(addr: address): bool;

通过使用泛型,此函数成为独立于类型的函数,你可以使用任何 Resource 类型来检查其是否存在于给定地址下。实际上,任何人都可以检查给定地址处是否存在 Resource。但是检查是否存在并不意味着能获取储 Resource !

让我们编写一个函数来检查用户是否已经拥有 resource T:

代码语言:javascript
复制
// sources/Collection.move
module std::Collection {
    use std::vector;

    struct Item has store,drop {}

    struct Collection has key {
        items: vector<Item>
    }

    // note that &signer type is passed here!
    public fun start_collection(account: &signer) {
        move_to<Collection>(account, Collection {
            items: vector::empty<Item>()
        })
    }
    // this function will check if resource exists at address
    public fun exists_at(at: address): bool {
        exists<Collection>(at)
    }
}

现在你已经知道了如何创建 Resource,如何将其移动到发送者账户下以及如何检查 Resource 是否已经存在,现在是时候学习如何访问和修改 Resource 了。

读取和修改 Resource

Move 有两个内建函数用来读取和修改 Resource。它们的功能就像名字一样:borrow_global 和 borrow_global_mut。

不可变借用 borrow_global

可变引用(&mut)和不可变的引用(&)

代码语言:javascript
复制
// sources/Collection.move
module std::Collection {
    use std::vector;
    use std::signer;

    struct Item has store,drop {}

    struct Collection has key {
        items: vector<Item>
    }

    // note that &signer type is passed here!
    public fun start_collection(account: &signer) {
        move_to<Collection>(account, Collection {
            items: vector::empty<Item>()
        })
    }

    // this function will check if resource exists at address
    public fun exists_at(at: address): bool {
        exists<Collection>(at)
    }

    // get collection size
    // mind keyword acquires!
    public fun size(account: &signer): u64 acquires Collection {
        let owner = signer::address_of(account);
        let collection = borrow_global<Collection>(owner);

        vector::length(&collection.items)
    }
}

这里发生了很多事情。首先,让我们看一下函数的签名。全局函数 borrow_global 返回了对 Resource T 的不可变引用。其签名如下:

代码语言:javascript
复制
native fun borrow_global<T: key>(addr: address): &T;

通过使用此功能,我们可以读取存储在特定地址的 Resource。这意味着该模块(如果实现了此功能)具有读取任何地址上任何 Resource 的能力,当然这里的 Resource 指的是该模块内定义的任何 Resource。

另一个结论:由于 Borrow检查,你不能返回对 Resource 的引用或对其内容的引用(因为对 Resource 的引用将在函数作用域结束时消失)。

由于 Resource 是不可复制的类型,因此不能在其上使用取值运算符 “*”。

Acquires 关键字

还有另一个值得解释的细节:关键字 acquires。该关键字放在函数返回值之后,用来显式定义此函数获取的所有 Resource。我们必须指定所有获取的 Resource,即使它实际上是子函数所获取的 Resource,即父函数必须在其获取列表中包含子函数的获取列表。

acquires 使用方法如下:

代码语言:javascript
复制
fun <name>(<args...>): <ret_type> acquires T, T1 ... {

可变借用 borrow_global_mut

要获得对 Resource 的可变引用,请添加_mutborrow_global 后,仅此而已。让我们添加一个函数,将新的 Item 添加到集合中。

代码语言:javascript
复制
// sources/Collection.move
module std::Collection {

    // ... skipped ...

    // add item to collection
    public fun add_item(account: &signer) acquires Collection {
        let collection = borrow_global_mut<Collection>(signer::address_of(account));
        vector::push_back(&mut collection.items, Item {});
    }
}

对 Resource 的可变引用允许创建对其内容的可变引用。这就是为什么我们可以在此示例中修改内部向量 items 的原因。

代码语言:javascript
复制
native fun borrow_global_mut<T: key>(addr: address): &mut T;

使用和销毁

取出和销毁 Resource

move_from,它用来将 Resource 从账户下取出。我们将实现 destroy 函数,将Collection 的 T Resource 从账户取出并且销毁它的内容。

代码语言:javascript
复制
// sources/Collection.move
module std::Collection {

   // ... skipped ...

   // remove item from collection
   public fun destroy(account: &signer) acquires Collection {

       // account no longer has resource attached
       let collection = move_from<Collection>(signer::address_of(account));

       // now we must use resource value - we'll destructure it
       // look carefully - Items must have drop ability
       let Collection { items: _ } = collection;

       // done. resource destroyed
   }
}

Resource 必需被使用。因此,从账户下取出 Resource 时,要么将其作为返回值传递,要么将其销毁。但是请记住,即使你将此 Resource 传递到外部并在脚本中获取,接下来能做的操作也非常有限。因为脚本上下文不允许你对结构体或 Resource 做任何事情,除非 Resource 模块中定义了操作 Resource 公开方法,否则只能将其传递到其它地方。知道这一点,就要求我们在设计模块时,为用户提供操作 Resource 的函数。

move_from 函数签名:

代码语言:javascript
复制
native fun move_from<T: key>(addr: address): T;

参考资料

[1]

木头: https://learnblockchain.cn/people/3015

[2]

Resource: https://learnblockchain.cn/article/4519

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 深入浅出区块链技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义
  • Resource 的限制
  • 创建和移动 Resource
    • 创建和移动
      • 查看 Resource 是否存在
      • 读取和修改 Resource
        • 不可变借用 borrow_global
          • Acquires 关键字
        • 可变借用 borrow_global_mut
        • 使用和销毁
          • 取出和销毁 Resource
            • 参考资料
            相关产品与服务
            对象存储
            对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档