上一章我們簡單概括了四種不同類型的對象,這一章將詳細(xì)介紹每種對象的使用方法。期間可能會有些關(guān)于ability的內(nèi)容,如果對ability不太熟悉的朋友,建議先看:9.輕松入門Sui Move: Ability
獨(dú)有對象
獨(dú)有對象屬于某一個賬戶地址或者某個對象ID,只有該賬戶地址(或?qū)ο螅┠茉L問,修改,刪除和轉(zhuǎn)交它。
創(chuàng)建方法
創(chuàng)建一個對象后,使用transfer或者public_transfer把所有權(quán)轉(zhuǎn)交給一個賬戶地址(或?qū)ο驣D),那么這個對象就是獨(dú)有對象。
public entry fun new(ctx: &mut TxContext) {
let person = Person {
id: object::new(ctx),
name: string::utf8(b"hanmeimei"),
};
let company = Company {
id: object::new(ctx),
person: person,
can_be_transfered: false,
};
//company就是一個獨(dú)有對象
transfer::transfer(company, tx_context::sender(ctx))
}
那transfer和public_transfer有什么區(qū)別呢?我們來看一下實(shí)現(xiàn)這兩個方法的源碼:
public fun transfer<T: key>(obj: T, recipient: address) {
transfer_impl(obj, recipient)
}
//public_transfer要求被轉(zhuǎn)交的對象具有key, store ability
public fun public_transfer<T: key + store>(obj: T, recipient: address) {
transfer_impl(obj, recipient)
}
我們可以看到函數(shù)內(nèi)的實(shí)現(xiàn)完全一樣,只是不同函數(shù)對類型T的限定有區(qū)別:public_transfer方法還要求對象必須有store ability。
根據(jù)這個源碼,我們得出以下結(jié)論:
- 無論是transfer還是pubic_transfer,都只能用于轉(zhuǎn)交對象,沒有key ability的非對象結(jié)構(gòu)體不能使用。
- 如果對象沒有store ability 就只能使用transfer
還有一個區(qū)別就是transfer方法只能在定義對象的模塊內(nèi)使用,public_transfer則模塊內(nèi)外均可使用。
總結(jié)來說就是:transfer適用于沒有store ability的對象,只能在定義它的模塊內(nèi)轉(zhuǎn)交。 public_transfer則只能用于有store ability的對象,可以在模塊內(nèi)外轉(zhuǎn)交。
<font size=4>筆者做了一個小實(shí)驗(yàn),使用transfer在模塊內(nèi)轉(zhuǎn)交有store ability的對象,會觸發(fā)警告,不影響運(yùn)行。但不建議這么做。</font>
使用場景
只要是在任何時間點(diǎn)都只有一個擁有者的對象,都應(yīng)該使用獨(dú)有對象。相比共享對象,獨(dú)有對象沒有數(shù)據(jù)爭用的問題,將會有更快的速度,更小的成本和更高的吞吐量,所以獨(dú)有對象應(yīng)用盡用。
不可變對象
不可變對象不能被編輯、刪除和轉(zhuǎn)交。它不屬于任何人,所有人都可以訪問它。
創(chuàng)建方法
使用freeze_object或者public_freeze_object就可以freeze對象,把對象變成不可變對象。注意!這個過程是不可逆的。
//創(chuàng)建一個不可變對象
public fun new_contract(text:String, ctx: &mut TxContext) {
transfer::freeze_object(Contract{
id: object::new(ctx),
text: string::utf8(b"hello world"),
})
}
freeze_object和public_freeze_object的區(qū)別,跟public_transfer和transfer的區(qū)別一樣,這里不再詳解。
訪問方法
因?yàn)椴豢勺儗ο蟛粚儆谌魏稳?,也不允許編輯,所以不可變對象只允許不可變引用。把對象本身作為參數(shù)或者使用可變引用都會引起報錯,
//正確
public fun access_contract(c: &Contract, _: &mut TxContext)
//報錯
public fun access_contract(c: Contract, _: &mut TxContext)
//報錯
public fun access_contract(c: &mut Contract, _: &mut TxContext)
使用場景
只要對象有不可改變,不能轉(zhuǎn)移,不能刪除的特性都應(yīng)該使用不可變對象。不僅是因?yàn)樗奶匦?,而且它也沒有數(shù)據(jù)爭用問題具有較高性能。一個典型的不可變對象就是我們發(fā)布的包。
共享對象
共享對象是使用share_object或者public_share_object函數(shù)的對象,它屬于所有人,所有人都可以訪問、修改、刪除和轉(zhuǎn)交它。這里值得注意的是,所有人都可以訪問共享對象不代表模塊內(nèi)外都能直接訪問對象內(nèi)字段,模塊外對共享對象字段的訪問也只能通過調(diào)用定義對象的模塊提供的函數(shù)實(shí)現(xiàn)。所有人都可以轉(zhuǎn)交共享對象也不意味著模塊外一定能轉(zhuǎn)交共享對象,模塊外要能轉(zhuǎn)交共享對象,要求對象有store abiity 并且使用public_share_object方法。
share_object和public_share_object的區(qū)別請看transfer和public_transfer
創(chuàng)建方法
public fun new_platform(ctx: &mut TxContext): Admin {
let platform = RentalPlatform {
id: object::new(ctx),
deposit_pool: table::new<ID, u64>(ctx),
balance: balance::zero<SUI>(),
notices: table::new<ID, RentalNotice>(ctx),
owner: tx_context::sender(ctx),
};
transfer::share_object(platform);
}
訪問方法
//這三種都可以
public fun access_platform(c: &Contract, _: &mut TxContext)
public fun access_platform(c: Contract, _: &mut TxContext)
public fun access_platform(c: &mut Contract, _: &mut TxContext)
使用場景
只有必要的時候才使用共享對象,因?yàn)樗腥硕寄芫庉?,轉(zhuǎn)交,刪除可能會存在數(shù)據(jù)爭用問題,為了解決數(shù)據(jù)爭用會耗費(fèi)更多時間和資源。
被嵌套的對象
被嵌套的對象被一個對象嵌套在內(nèi),成為外層對象的一部分,在鏈上不能獨(dú)立存在,也無法使用對象ID直接訪問。只能通過嵌套他的對象訪問,修改或轉(zhuǎn)移。如果它又被轉(zhuǎn)交給了一個賬戶地址,這個對象就轉(zhuǎn)變成了獨(dú)有對象,該賬戶的用戶就可以通過對象ID直接訪問了。
嵌套對象的Owner是外層對象ID,那是不是Owner是對象ID的都是嵌套對象呢???別忘了獨(dú)有對象的owner也可能是對象ID,這里需要注意區(qū)分。
注意,非對象結(jié)構(gòu)體也能被嵌套,嵌套時也要求結(jié)構(gòu)體有store ability.但是本文討論的是對象,非對象結(jié)構(gòu)體暫不詳談。
嵌套的方式有三種,分別是直接嵌套,通過Option嵌套和通過vector嵌套。我們接下來依次探究每種嵌套方式。
直接嵌套
創(chuàng)建方式
直接把一個對象作為另外一個對象的字段,這種方式就是直接嵌套。
public struct Company has key,store {
id: UID,
//嵌套Person對象
person: Person,
can_be_transfered: bool,
}
public struct Person has key,store {
id:UID,
name: String,
}
注意,不能循環(huán)嵌套,也就是說A嵌套B,B不能嵌套A。
訪問方式
被直接嵌套的對象,不能使用對象ID訪問(sui client object命令也不行),也不能在任何函數(shù)調(diào)用中將其作為參數(shù),唯一的訪問方式就是通過訪問外層對象。
public entry fun access_person(company: &Company,_: &mut TxContext) {
//使用.一層一層訪問
let _ = company.person.name;
}
解除嵌套
被嵌套的對象,可以取出轉(zhuǎn)交給賬戶地址,轉(zhuǎn)變成一個獨(dú)有對象。這個過程稱之為解除嵌套。方法如下:
//這里的company對象必須按值傳遞才能獲取到person的對象
public entry fun transfer_person(company: Company, ctx: &mut TxContext) {
let Company{
id:id,
person:person,
can_be_transfered:can_be_transfered,
} = company;
let _ = can_be_transfered;
//需要使用person對象值來轉(zhuǎn)交
transfer::public_transfer(person, tx_context::sender(ctx));
//必須刪除外層對象
object::delete(id);
}
本段代碼執(zhí)行完,輸出的Transaction Effects 模塊,會有一個 Unwrapped Objects,展示的就是解開嵌套的對象。如下:

應(yīng)用場景
被嵌套的對象無法直接訪問只有通過調(diào)用模塊內(nèi)特定的函數(shù)才能訪問,在這個函數(shù)內(nèi)我們可以設(shè)置訪問的條件,以實(shí)現(xiàn)對對象的訪問限制。
被直接嵌套的對象在解除嵌套的時候,必須刪除外層對象;并且實(shí)例化外層對象的時候也必須同時實(shí)例化被嵌套的對象。直接嵌套適用于外層對象必須有嵌套對象的場景。比如公司必須有員工,沒員工就會倒閉。
那有沒有一種更靈活的方式,外層對象可能嵌套對象,可能沒有嵌套對象,可以動態(tài)嵌套;解除嵌套也無需刪除外層對象?通過Option嵌套就能輕松實(shí)現(xiàn)。
通過Option嵌套
Option是標(biāo)準(zhǔn)庫實(shí)現(xiàn)的一個結(jié)構(gòu)體,含有copy,drop和store ability,內(nèi)部包含一個可指定類型的數(shù)組字段。源碼如下:
//在使用Option類型的時候,需要使用<>指定類型。這是泛型相關(guān)知識后續(xù)將會講到
struct Option<Element> has copy, drop, store {
vec: vector<Element>
}
那如何使用Option來嵌套對象呢?我們通過一個人和筆記本的例子說明:
創(chuàng)建方式
有的人擁有筆記本,有的人則沒有。所以我們可以聲明一個Person對象,通過Option嵌套Notebook對象。我們先實(shí)例化一個沒有筆記本的Person對象:
public struct Person has key {
id: UID,
name: String,
notebook: Option<Notebook>,
}
//被嵌套的對象必須要有store ability
public struct Notebook has key,store {
id: UID,
brand: String,
model: String,
}
public fun new(ctx: &mut TxContext) {
transfer::transfer(Person{
id: object::new(ctx),
name: string::utf8(b"hanmeimei"),
//可以實(shí)例化一個沒有Notebook對象的Peroson對象
notebook: option::none<Notebook>(),
}, tx_context::sender(ctx));
}
后面這位名為hanmeimei的人購買了一個筆記本,我們使用option::fill方法將Notebook對象嵌入Person對象:
//嵌套Notebook對象
public fun fill_notebook(person: &mut Person, ctx: &mut TxContext) {
//在將Notebook嵌入Person之前,確定Person沒有嵌套Notebook。否則會報錯
assert!(option::is_none(&person.notebook), EOptionNotEmpty);
let notebook = Notebook {
id: object::new(ctx),
brand: string::utf8(b"HUAWEI"),
model: string::utf8(b"v10"),
};
//嵌套Notebook
option::fill<Notebook>(&mut person.notebook, notebook);
}
訪問方式
訪問被Option嵌套的對象也只能通過外層對象訪問,可以使用option::borrow方法不可變引用notebook對象,也可以使用option::borrow_mut方法可變引用notebook對象:
public entry fun access_notebook(person: &Person, _: &mut TxContext) {
let notebook_ref = option::borrow<Notebook>(&person.notebook);
_ = notebook_ref.brand;
}
解除嵌套
如果要轉(zhuǎn)賣筆記本,則使用option::extract取出,并轉(zhuǎn)交給其他人。
//解除嵌套
public entry fun unwrap_notebook(person: &mut Person, ctx: &mut TxContext) {
//確認(rèn)有嵌套,否則會報錯
assert!(option::is_some(&person.notebook), EOptionEmpty);
//解除嵌套并轉(zhuǎn)交給當(dāng)前用戶
let notebook = option::extract<Notebook>(&mut person.notebook);
transfer::public_transfer(notebook, someone);
}
應(yīng)用場景
雖然Option類型的唯一字段是數(shù)組類型,但是Option<element>只能最多只能包含一個對象。所以O(shè)ption適合可能有一個嵌套或者沒有嵌套對象的場景。那如果我想嵌套多個同類型對象怎么辦?我們可以使用vector嵌套對象。
通過vector嵌套
很多人可能沒有筆記本,開發(fā)人員則可能有一個或者多個筆記本,我們使用vector來創(chuàng)建Person對象:
創(chuàng)建方式
我們可以聲明一個Person對象,notebooks字段申明為Notebook類型的數(shù)組。并實(shí)例化一個沒有筆記本的Person對象:
public struct Person has key {
id: UID,
name: String,
notebooks: vector<Notebook>,
}
public struct Notebook has key,store {
id: UID,
brand: String,
model: String,
}
public fun new(ctx: &mut TxContext) {
transfer::transfer(Person{
id: object::new(ctx),
name: string::utf8(b"hanmeimei"),
notebooks: vector::empty<Notebook>(),
}, tx_context::sender(ctx));
}
訪問方式
跟option類型類似,可以使用borrow方法不可變引用。不過因?yàn)槭菙?shù)組,在訪問的時候需要指定索引,確實(shí)訪問多個筆記本中的哪一個。
public entry fun access_notebook(person: &Person, index: u64, _: &mut TxContext) {
//也可以使用vector::borrow_mut方法可變引用
let notebook_ref = vector::borrow<Notebook>(&person.notebooks, index);
_ = notebook_ref.brand;
}
解除嵌套
使用vector::remove方法解除嵌套
//解除嵌套
public entry fun unwrap_notebook(person: &mut Person, notebook: &Notebook, ctx: &mut TxContext) {
//確認(rèn)有嵌套,否則會報錯
let (contains,index) = vector::index_of<Notebook>(&person.notebooks, notebook);
assert!(contains, EEmpty);
//解除嵌套并轉(zhuǎn)交給當(dāng)前用戶
let notebook = vector::remove<Notebook>(&mut person.notebooks, index);
transfer::public_transfer(notebook, tx_context::sender(ctx));
}
應(yīng)用場景
vector方法的嵌套,適用于外層對象有零個或者多個同類型嵌套對象的場景。
了解更多Sui Move內(nèi)容:
- telegram: t.me/move_cn
- QQ群: 79489587