Skip to content

Commit d8487f9

Browse files
committed
add std dynamic_field intro
1 parent 6492171 commit d8487f9

File tree

2 files changed

+357
-0
lines changed

2 files changed

+357
-0
lines changed

src/05_collection/01.dynamic_field.md

+344
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
# dynamic_field
2+
3+
## 模块说明
4+
5+
`dynamic_field(动态字段)`模块定义将结构体和值组合在一起的方式,可以在运行时动态进行添加和删除。任何具有 `store` 能力的值都可以被存储。可以不具备`key`能力,即不可直接从外部进行访问。
6+
7+
## 源码路径
8+
9+
https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/dynamic_field.move
10+
11+
## 方法图解
12+
13+
![](images/dynamic_field.svg)
14+
15+
## 方法说明
16+
17+
| 分类 | 方法 | 说明 |
18+
| ------ | ------------------------------------------------------ | ------------------------------------------------------------ |
19+
| **** | `add<...>(object: &mut UID, name: Name, value: Value)` | 向对象`object`添加名为`name`的值为`value`的动态字段 |
20+
| **** | `remove<...>(object: &mut UID, name: Name)` | 从对象`object`中删除名为`name`的动态字段,若不存在将会报错 |
21+
| | `remove_if_exists<...>(object: &mut UID, name: Name)` | 从对象`object`中删除名为`name`的动态字段,若存在则已`option::some`包装后返回,若不存在返回`option::none()` |
22+
| **** | `borrow_mut<...>(object: &mut UID, name: Name)` | 从对象`object`中获取名为`name`的动态字段的可变引用,以便进行对动态字段的修改 |
23+
| **** | `borrow<...>(object: &UID, name: Name)` | 从对象`object`中获取名为`name`的动态字段的只读引用,用于进行信息查看 |
24+
| | `exists_<...>(object: &UID, name: Name)` | 若对象`object`中存在名为`name`的动态字段则返回`true`,无需指定`value`类型 |
25+
| | `exists_with_type<...>(object: &UID, name: Name)` | 若对象`object`中存在名为`name`的动态字段则返回`true`,需指定`value`类型 |
26+
27+
## 代码示例
28+
29+
采用书架和书本的示例,书本对象作为动态字段添加到书架上。
30+
31+
### 结构定义
32+
33+
```rust
34+
// 书架结构定义
35+
public struct Bookshelf has key {
36+
id: UID,
37+
// 书本数量
38+
book_count: u64
39+
}
40+
41+
// 书本结构定义
42+
public struct Book has store {
43+
// 书本标题
44+
title: String,
45+
// 书本描述
46+
description: String,
47+
}
48+
```
49+
50+
### 创建书架共享对象
51+
52+
```rust
53+
public fun create_bookshelf(ctx: &mut TxContext) {
54+
transfer::share_object(Bookshelf {
55+
id: object::new(ctx),
56+
book_count: 0,
57+
});
58+
}
59+
```
60+
61+
### 添加书本到书架
62+
63+
> 调用`dynamic_field::add`方法。
64+
65+
```rust
66+
public fun add_book(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
67+
let book = Book {
68+
title: ascii::string(title),
69+
description: ascii::string(description)
70+
};
71+
72+
dynamic_field::add(&mut bookshelf.id,title, book);
73+
bookshelf.book_count = bookshelf.book_count + 1;
74+
}
75+
```
76+
77+
### 获取书本
78+
79+
> 调用`dynamic_field::borrow`方法。
80+
81+
```rust
82+
public fun get_book(bookshelf: &Bookshelf, title: vector<u8>): &Book {
83+
dynamic_field::borrow(&bookshelf.id, title)
84+
}
85+
```
86+
87+
### 设置书本的描述信息
88+
89+
> 调用`dynamic_field::borrow_mut`方法。
90+
91+
```rust
92+
public fun set_book_desc(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
93+
let book_mut_ref: &mut Book = dynamic_field::borrow_mut(&mut bookshelf.id, title);
94+
book_mut_ref.description = ascii::string(description);
95+
}
96+
```
97+
98+
### 判断书本是否存在
99+
100+
> 调用`dynamic_field::exists_``dynamic_field::exists_with_type`方法。
101+
102+
```rust
103+
public fun is_book_existed(bookshelf: &Bookshelf, title: vector<u8>): bool {
104+
dynamic_field::exists_(&bookshelf.id, title)
105+
}
106+
107+
public fun is_book_exists_with_type(bookshelf: &Bookshelf, title: vector<u8>): bool {
108+
dynamic_field::exists_with_type<vector<u8>, Book>(&bookshelf.id, title)
109+
}
110+
```
111+
112+
### 从书架上移除书本
113+
114+
> 调用`dynamic_field::remove``dynamic_field::remove_if_exists`方法。
115+
116+
```rust
117+
public fun remove_book(bookshelf: &mut Bookshelf, title: vector<u8>): Book {
118+
bookshelf.book_count = bookshelf.book_count - 1;
119+
dynamic_field::remove<vector<u8>, Book>(&mut bookshelf.id, title)
120+
}
121+
122+
public fun remove_if_book_exists(bookshelf: &mut Bookshelf, title: vector<u8>): Option<Book> {
123+
bookshelf.book_count = bookshelf.book_count - 1;
124+
dynamic_field::remove_if_exists<vector<u8>, Book>(&mut bookshelf.id, title)
125+
}
126+
```
127+
128+
## 完整代码
129+
130+
```rust
131+
module cookbook::dynamic_field{
132+
use std::ascii::{Self, String};
133+
use sui::dynamic_field;
134+
135+
public struct Bookshelf has key {
136+
id: UID,
137+
book_count: u64
138+
}
139+
140+
public struct Book has store {
141+
title: String,
142+
description: String,
143+
}
144+
145+
// 创建书架共享对象
146+
public fun create_bookshelf(ctx: &mut TxContext) {
147+
transfer::share_object(Bookshelf {
148+
id: object::new(ctx),
149+
book_count: 0,
150+
});
151+
}
152+
153+
// 添加书本到书架
154+
public fun add_book(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
155+
let book = Book {
156+
title: ascii::string(title),
157+
description: ascii::string(description)
158+
};
159+
160+
dynamic_field::add<vector<u8>, Book>(&mut bookshelf.id,title, book);
161+
bookshelf.book_count = bookshelf.book_count + 1;
162+
}
163+
164+
public fun add_book_obj(bookshelf: &mut Bookshelf, book: Book) {
165+
dynamic_field::add<vector<u8>, Book>(&mut bookshelf.id,
166+
book.title.into_bytes(), book);
167+
bookshelf.book_count = bookshelf.book_count + 1;
168+
}
169+
170+
// 获取书本
171+
public fun get_book(bookshelf: &Bookshelf, title: vector<u8>): &Book {
172+
dynamic_field::borrow(&bookshelf.id, title)
173+
}
174+
175+
// 设置书本的描述信息
176+
public fun set_book_desc(bookshelf: &mut Bookshelf, title: vector<u8>, description: vector<u8>) {
177+
let book_mut_ref: &mut Book = dynamic_field::borrow_mut(&mut bookshelf.id, title);
178+
book_mut_ref.description = ascii::string(description);
179+
}
180+
181+
// 判断书本是否存在
182+
public fun is_book_existed(bookshelf: &Bookshelf, title: vector<u8>): bool {
183+
dynamic_field::exists_(&bookshelf.id, title)
184+
}
185+
186+
public fun is_book_exists_with_type(bookshelf: &Bookshelf, title: vector<u8>): bool {
187+
dynamic_field::exists_with_type<vector<u8>, Book>(&bookshelf.id, title)
188+
}
189+
190+
// 从书架上移除书本
191+
public fun remove_book(bookshelf: &mut Bookshelf, title: vector<u8>): Book {
192+
bookshelf.book_count = bookshelf.book_count - 1;
193+
dynamic_field::remove<vector<u8>, Book>(&mut bookshelf.id, title)
194+
}
195+
196+
// 如果存在指定标题书本,则从书架上移除书本
197+
public fun remove_if_book_exists(bookshelf: &mut Bookshelf, title: vector<u8>): Option<Book> {
198+
bookshelf.book_count = bookshelf.book_count - 1;
199+
dynamic_field::remove_if_exists<vector<u8>, Book>(&mut bookshelf.id, title)
200+
}
201+
202+
public fun get_book_count(bookshelf: &Bookshelf): u64{
203+
bookshelf.book_count
204+
}
205+
206+
public fun get_book_title(book: &Book): String {
207+
book.title
208+
}
209+
210+
public fun get_book_desc(book: &Book): String {
211+
book.description
212+
}
213+
}
214+
```
215+
216+
## 单元测试
217+
218+
```rust
219+
#[test_only]
220+
module cookbook::dynamic_field_test {
221+
use std::ascii;
222+
use sui::test_scenario as ts;
223+
use cookbook::dynamic_field::{Bookshelf, create_bookshelf, add_book, get_book,
224+
set_book_desc, is_book_existed, is_book_exists_with_type, remove_book,
225+
get_book_count, get_book_title, get_book_desc};
226+
227+
#[test_only]
228+
use sui::test_utils::assert_eq;
229+
230+
#[test]
231+
public fun test_dynamic_field() {
232+
let alice = @0xa;
233+
234+
let mut ts = ts::begin(alice);
235+
236+
// 创建书架
237+
{
238+
create_bookshelf(ts.ctx());
239+
};
240+
241+
// 添加书本到书架
242+
let expected_title = b"Mastering Bitcoin";
243+
let expected_description= b"1st Edition";
244+
let expected_new_description= b"3rd Edition";
245+
246+
{
247+
ts.next_tx(alice);
248+
let mut bookshelf: Bookshelf = ts.take_shared();
249+
250+
add_book(
251+
&mut bookshelf,
252+
expected_title,
253+
expected_description,
254+
);
255+
256+
assert_eq(bookshelf.get_book_count(), 1);
257+
258+
ts::return_shared(bookshelf);
259+
};
260+
261+
// 获取书本
262+
{
263+
ts.next_tx(alice);
264+
let bookshelf: Bookshelf = ts.take_shared();
265+
266+
let book = get_book(
267+
&bookshelf,
268+
expected_title,
269+
);
270+
271+
assert_eq(book.get_book_title(), ascii::string(expected_title));
272+
assert_eq(book.get_book_desc(), ascii::string(expected_description));
273+
274+
ts::return_shared(bookshelf);
275+
};
276+
277+
// 设置书本的描述信息
278+
{
279+
ts.next_tx(alice);
280+
let mut bookshelf: Bookshelf = ts.take_shared();
281+
282+
set_book_desc(
283+
&mut bookshelf,
284+
expected_title,
285+
expected_new_description,
286+
);
287+
288+
let book = get_book(
289+
&bookshelf,
290+
expected_title,
291+
);
292+
293+
assert_eq(book.get_book_title(), ascii::string(expected_title));
294+
assert_eq(book.get_book_desc(), ascii::string(expected_new_description));
295+
296+
ts::return_shared(bookshelf);
297+
};
298+
299+
// 判断书本是否存在
300+
{
301+
ts.next_tx(alice);
302+
let bookshelf: Bookshelf = ts.take_shared();
303+
304+
let is_existed = is_book_existed(
305+
&bookshelf,
306+
expected_title,
307+
);
308+
assert_eq(is_existed, true);
309+
310+
let is_existed = is_book_exists_with_type(
311+
&bookshelf,
312+
expected_title,
313+
);
314+
assert_eq(is_existed, true);
315+
316+
ts::return_shared(bookshelf);
317+
};
318+
319+
// 从书架上移除书本
320+
{
321+
ts.next_tx(alice);
322+
let mut bookshelf: Bookshelf = ts.take_shared();
323+
324+
assert_eq(bookshelf.get_book_count(), 1);
325+
326+
let book = remove_book(
327+
&mut bookshelf,
328+
expected_title,
329+
);
330+
331+
assert_eq(bookshelf.get_book_count(), 0);
332+
assert_eq(book.get_book_title(), ascii::string(expected_title));
333+
assert_eq(book.get_book_desc(), ascii::string(expected_new_description));
334+
335+
bookshelf.add_book_obj(book);
336+
337+
ts::return_shared(bookshelf);
338+
};
339+
340+
ts.end();
341+
}
342+
}
343+
```
344+

0 commit comments

Comments
 (0)