Move 白皮书中详细描述了 Resource这个概念。最初,它是作为一种名为resource的结构体类型被实现,自从引入 ability 以后,它被实现成拥有 Key
和 Store
两种 ability 的结构体。Resource`可以安全的表示数字资产,它不能被复制,也不能被丢弃或重新使用,但是它却可以被安全地存储和转移。
Resource 是一种用 key
和 store
ability
限制了的结构体:
module M {
struct T has key, store {
field: u64
}
}
在代码中,Resource 类型有几个主要限制:
kind
:resource
,它与copyable
不同。首先,让我们创建模块:
// 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。// 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 函数的签名可以表示为:
```js
native fun move_to<T: key>(account: &signer, value: T);
总结一下上面所学的内容:
Move 提供 exists
函数来查看某 Resource 是否存在于给定地址下,函数签名如下:
native fun exists<T: key>(addr: address): bool;
通过使用泛型,此函数成为独立于类型的函数,你可以使用任何 Resource 类型来检查其是否存在于给定地址下。实际上,任何人都可以检查给定地址处是否存在 Resource。但是检查是否存在并不意味着能获取储 Resource !
让我们编写一个函数来检查用户是否已经拥有 resource T:
// 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 了。
Move 有两个内建函数用来读取和修改 Resource。它们的功能就像名字一样:borrow_global 和 borrow_global_mut。
可变引用(&mut)和不可变的引用(&)
// 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 的不可变引用。其签名如下:
native fun borrow_global<T: key>(addr: address): &T;
通过使用此功能,我们可以读取存储在特定地址的 Resource。这意味着该模块(如果实现了此功能)具有读取任何地址上任何 Resource 的能力,当然这里的 Resource 指的是该模块内定义的任何 Resource。
另一个结论:由于 Borrow
检查,你不能返回对 Resource 的引用或对其内容的引用(因为对 Resource 的引用将在函数作用域结束时消失)。
由于 Resource 是不可复制的类型,因此不能在其上使用取值运算符 “*”。
还有另一个值得解释的细节:关键字 acquires。该关键字放在函数返回值之后,用来显式定义此函数获取的所有 Resource。我们必须指定所有获取的 Resource,即使它实际上是子函数所获取的 Resource,即父函数必须在其获取列表中包含子函数的获取列表。
acquires 使用方法如下:
fun <name>(<args...>): <ret_type> acquires T, T1 ... {
要获得对 Resource 的可变引用,请添加_mut
到borrow_global
后,仅此而已。让我们添加一个函数,将新的 Item 添加到集合中。
// 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 的原因。
native fun borrow_global_mut<T: key>(addr: address): &mut T;
move_from
,它用来将 Resource 从账户下取出。我们将实现 destroy
函数,将Collection
的 T Resource 从账户取出并且销毁它的内容。
// 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
函数签名:
native fun move_from<T: key>(addr: address): T;
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!