「资源」是 Move 语言中最关键的概念。本篇将以 Library 图书馆 Demo 为例,讲解在 Rust 下是如何操作资源的。
0x01 源码下载
本文的 Demo 源码包含 smart contract、 react dApp、 script.sh三部分,资源链接如下:
0x02 Types with Abilities
Move 的类型系统非常灵活,每种类型都可以定义四种能力(abilities)。它们决定了类型的值是否可以被「使用、丢弃和存储」。
它们的功能分别是:
Copy - 值可以被复制。
Drop - 在作用域(Scope)结束时值可以被丢弃。
Key - 值可以作为键值(Key)被「全局存储操作( global storage operations)」进行访问。
Store - 值可以被 存储 到全局状态。
在上一篇中,我们已经初步接触到了 Abilities。在本实例中,我们将进一步地通过 Play with Abilities 掌握其原理。
0x03 Abilities 的语法
然而,结构体的 ability 可以按照下面的语法进行添加:
一个图书馆的Struct例子:
0x04 Library 合约实践
合约源码见:
对应的脚本合集如下(脚本中的路径、地址等信息需自行调整):
通过如下命令在starcoin console中进行合约部署:
初始化 Library:
查看 Library:
此时因为还没有藏书,所以图书馆是空的:
插入一本 Book:
此时再执行get resource,会发现多了一个 item:
更新(Update)和删除(Delete)的操作同理,你自己来动手试试吧:)!
0x05 Library 合约源码分析
Move 合约包含Struct、func和script三个部分。
其中,Struct定义数据结构、func是一般函数、script是暴露被外部调用的脚本。
use StarcoinFramework::Signer;
use StarcoinFramework::Vector;
// each ability has matching keyword
// multiple abilities are listed with comma
struct Book has store, copy, drop {
id: u64,
name: vector<u8>,
link: vector<u8>
}
// single ability is also possible
struct Library has key {
books: vector<Book>
}
public fun create_library(account: &signer){
move_to<Library>(account, Library{books: Vector::empty<Book>()});
}
//because the script function cannot have return value,
//query only can be done by: state get resource Addr Addr::MyLibraryV4::Library
public fun addBook(account: &signer,name:vector<u8>, link: vector<u8>) acquires Library {
let lib = borrow_global_mut<Library>(Signer::address_of(account));
let id = Vector::length(&lib.books);
Vector::push_back(&mut lib.books, Book{id:id,name:name,link:link});
}
public fun updateBookAtId(account: &signer,id:u64,name:vector<u8>, link: vector<u8>) acquires Library {
let lib = borrow_global_mut<Library>(Signer::address_of(account));
let book = Vector::borrow_mut<Book>(&mut lib.books,id);
book.name = name;
book.link = link;
}
public fun deleteBookAtId(account: &signer,id:u64) acquires Library {
let lib = borrow_global_mut<Library>(Signer::address_of(account));
Vector::remove(&mut lib.books, id);
}
public(script) fun init_library(account: signer){
Self::create_library(&account)
}
public(script) fun s_add_book(account: signer, name:vector<u8>, link: vector<u8>) acquires Library {
Self::addBook(&account,name, link)
}
public(script) fun s_update_book_at_id(account: signer, id:u64,name:vector<u8>, link: vector<u8>) acquires Library {
Self::updateBookAtId(&account,id,name,link)
}
public(script) fun s_delete_book_at_id(account: signer, id:u64) acquires Library {
Self::deleteBookAtId(&account,id)
}
}
5.1 move_to 函数
还记得 signer 吗?现在你可以看看它是如何运作的!要将资源移动到帐户,您有内置函数 move_to,它将signer作为第一个参数,Collection作为第二个参数。move_to 函数的签名可以表示为:
这导致两个结论:
您只能将资源放在您的帐户下。您无法访问另一个帐户的 signer value,因此无法将资源放在那里。
一个地址下只能存储一种单一类型的资源。两次执行相同的操作会导致丢弃现有资源——这种情况绝不能发生(想象您存储了您的硬币,并且由于不准确的操作,您通过推空余额丢弃了所有储蓄!)。第二次尝试创建现有资源将失败并出现错误。
—— https://move-book.com/resources/resource-by-example/storing-new-resource.html
在本 demo 中,我们通过 move_to 函数创建了Library资源:
5.2 borrow_global & borrow_global_mut
5.3 Vector
Vector 是 Rust 中的一种数据类型,其允许我们在一个单独的数据结构中储存多于一个的值,它在内存中彼此相邻地排列所有的值。Vector 只能储存相同类型的值。它们在拥有一系列项的场景下非常实用,例如文件中的文本行或是购物车中商品的价格。
在本 Demo 中,我们使用vector来统一处理字符串,相当于 Python 中的 b"something",或 Elixir 中的 <<1,2,3>>。
此外,Library 中存放了很多书籍,我们能用vector表示,相当于其他语言中的List类型:
要了解更多关于 Vector 的内容,可以见starcoin-framework: