Skip to content

Commit 8adea60

Browse files
authored
[move-book] Extending Book by Move 2.0 Documentation (#567)
### Description Adds documentation derived from our internal preparation documents. Also adds an overview page for Move 2 which links to other sections in the book. One way to view this is via the latest vercel preview deployment which can be seen in the PR activity. ### Checklist [All CI tests passed] - Do all Lints pass? - [ ] Have you ran `pnpm spellcheck`? - [ ] Have you ran `pnpm fmt`? - [ ] Have you ran `pnpm lint`?
1 parent bd040f4 commit 8adea60

11 files changed

+478
-30
lines changed

apps/nextra/pages/en/build/smart-contracts/book/SUMMARY.mdx

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
- [Modules and Scripts](modules-and-scripts.mdx)
66
- [Move Tutorial](move-tutorial.mdx)
77

8+
## Language Release Notes
9+
10+
- [Move 2.0](move-2.0.mdx)
11+
812
## Primitive Types
913

1014
- [Integers](integers.mdx)
@@ -24,6 +28,7 @@
2428
- [While, For, and Loop](loops.mdx)
2529
- [Functions](functions.mdx)
2630
- [Structs and Resources](structs-and-resources.mdx)
31+
- [Enum Types](enums.mdx)
2732
- [Constants](constants.mdx)
2833
- [Generics](generics.mdx)
2934
- [Type Abilities](abilities.mdx)

apps/nextra/pages/en/build/smart-contracts/book/_meta.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ export default {
22
SUMMARY: {
33
title: "Summary",
44
},
5+
"move-2.0": {
6+
title: "Move 2.0 Release Notes",
7+
},
58
"---getting-started---": {
69
type: "separator",
710
title: "Getting Started",
@@ -62,6 +65,9 @@ export default {
6265
"structs-and-resources": {
6366
title: "Structs and Resources",
6467
},
68+
enums: {
69+
title: "Enum Types",
70+
},
6571
constants: {
6672
title: "Constants",
6773
},

apps/nextra/pages/en/build/smart-contracts/book/abort-and-assert.mdx

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ condition of type `bool` and a code of type `u64`
6464

6565
```move
6666
assert!(condition: bool, code: u64)
67+
assert!(condition: bool) // Since Move 2.0
6768
```
6869

6970
Since the operation is a macro, it must be invoked with the `!`. This is to convey that the
@@ -74,6 +75,10 @@ does not exist at the bytecode level. It is replaced inside the compiler with
7475
if (condition) () else abort code
7576
```
7677

78+
Since Move 2.0, `assert` without an error code is supported. If this assert is used, the
79+
abort code `0xCA26CBD9BE0B0000` is generated. In terms of the `std::error` convention, this code has
80+
category `std::error::INTERNAL` and reason `0`.
81+
7782
`assert` is more commonly used than just `abort` by itself. The `abort` examples above can be
7883
rewritten using `assert`
7984

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Enums
2+
3+
_Since language version 2.0_
4+
5+
Enum types are similar to struct types but support defining multiple *variants* of the data layout. Each variant has its distinct set of fields. Enum variants are supported in expressions, tools for testing, matching, and deconstructing.
6+
7+
8+
## Declaration of Enum Types
9+
10+
An enum type declaration lists the number of different variants, as seen in the example below:
11+
12+
```move
13+
enum Shape {
14+
Circle{radius: u64},
15+
Rectangle{width: u64, height: u64}
16+
}
17+
```
18+
19+
There can be zero or more fields for an enum variant. If no arguments are given, the braces can also be omitted, declaring simple values:
20+
21+
```move
22+
enum Color {
23+
Red, Blue, Green
24+
}
25+
```
26+
27+
Like struct types, enum types can have abilities. For example, the `Color` enum type would be appropriately declared as copyable, droppable, and storable, like primitive number types:
28+
29+
```move
30+
enum Color has copy, drop, store, key { Red, Blue, Green }
31+
```
32+
33+
Enum types can also have the `key` ability and appear as roots of data in global storage. A common usage of enums in this context is versioning of data:
34+
35+
```move
36+
enum VersionedData has key {
37+
V1{name: String}
38+
V2{name: String, age: u64}
39+
}
40+
```
41+
42+
Similar to structs, enum types can be generic and take positional arguments. For example, the type below represents a generic result type, where the variant constructors use positional instead of named arguments (see also [positional structs](structs-and-resources.mdx#positional-structs)).
43+
44+
```move
45+
enum Result<T> has copy, drop, store {
46+
Err(u64),
47+
Ok(T)
48+
}
49+
```
50+
51+
## Constructing Enum Values
52+
53+
An enum value is constructed similarly to a struct value:
54+
55+
```move
56+
let s: String;
57+
let data = VersionedData::V1{name: s};
58+
```
59+
60+
If the enum variant has no fields, the braces can also be omitted:
61+
62+
```move
63+
let color = Color::Blue;
64+
```
65+
66+
## Name Resolution for Enum Variants
67+
68+
The variant names for an enum need to be qualified by the enum type name, as in `VersionedData::V1`.
69+
70+
> Note: Aliasing via the `use` clause is currently not supported for enum variants, but will be added in later language versions
71+
72+
In certain cases (such as match expressions, below), the Move compiler can infer the enum type from the context, and the qualification by the type name may be omitted:
73+
74+
```move
75+
fun f(data: VersionedData) {
76+
match (data) { V1{..} => .., ..} // simple variant name OK
77+
}
78+
```
79+
80+
## Matching Enum Values
81+
82+
The value of an enum value can be inspected using a match expression. For example:
83+
84+
```move
85+
fun area(self: &Rectangle): u64 {
86+
match (self) {
87+
Circle{radius} => mul_with_pi(*radius * *radius),
88+
Rectangle{width, height) => *width * *height
89+
}
90+
}
91+
```
92+
93+
Notice above that the value matched is an immutable reference to an enum value. A match expression can also consume a value, or match over a mutable reference for interior updates:
94+
95+
```move
96+
fun scale_radius(self: &mut Rectangle, factor: u64) {
97+
match (self) {
98+
Circle{radius: r} => *r = *r * factor,
99+
_ => {} // do nothing if not a Circle
100+
}
101+
}
102+
```
103+
104+
The patterns provided in the match expression are evaluated sequentially, in order of textual occurrence, until a match is found. It is a compile time error if not all known patterns are covered.
105+
106+
Patterns can be nested and contain conditions, as in the following example:
107+
108+
```move
109+
let r : Result<Result<u64>> = Ok(Err(42));
110+
let v = match (r)) {
111+
Ok(Err(c)) if c < 42 => 0,
112+
Ok(Err(c)) if c >= 42 => 1,
113+
Ok(_) => 2,
114+
_ => 3
115+
};
116+
assert!(v == 1);
117+
```
118+
119+
Notice that in the above example, the last match clause (`_`) covers both patterns `Ok(Err(_))` and `Err(_)`. Although at execution time, the earlier clauses match `Ok(Err(c))` for all values of `c`, the compiler cannot be sure all cases are covered due to the conditionals: conditions in match expressions are not considered when tracking coverage. Thus the first two clauses in the match expression above are not sufficient for match completeness, and an additional clause is required to avoid a compiler error.
120+
121+
## Testing Enum Variants
122+
123+
With the `is` operator, one can examine whether a given enum value is of a given variant:
124+
125+
```move
126+
let data: VersionedData;
127+
if (data is VersionedData::V1) { .. }
128+
```
129+
130+
The operator allows specifying a list of variants, separated by "`|`" characters. The variants need not be qualified by the enum name if the type of the expression being tested is known:
131+
132+
```move
133+
assert!(data is V1|V2);
134+
```
135+
136+
## Selecting From Enum Values
137+
138+
It is possible to directly select a field from an enum value. Recall the definition of versioned data:
139+
140+
```move
141+
enum VersionedData has key {
142+
V1{name: String}
143+
V2{name: String, age: u64}
144+
}
145+
```
146+
147+
One can write code as below to directly select the fields of variants:
148+
149+
```move
150+
let s: String;
151+
let data1 = VersionedData::V1{name: s};
152+
let data2 = VersionedData::V2{name: s, age: 20};
153+
assert!(data1.name == data2.name)
154+
assert!(data2.age == 20);
155+
```
156+
157+
Notice that field selection aborts if the enum value has no variant with the given field. This is the case for `data1.age`.
158+
The abort code used for this case is `0xCA26CBD9BE0B0001`. In terms of the `std::error` convention, this code has
159+
category `std::error::INTERNAL` and reason `1`.
160+
161+
Field selection is only possible if the field is uniquely named and typed throughout all variants. Thus, the following yields a compile time error:
162+
163+
```move
164+
enum VersionedData has key {
165+
V1{name: String}
166+
V2{name: u64}
167+
}
168+
169+
data.name
170+
// ^^^^^ compile time error that `name` field selection is ambigious
171+
```
172+
173+
## Using Enums Patterns in Lets
174+
175+
An enum variant pattern may be used in a `let` statement:
176+
177+
```move
178+
let data: VersionData;
179+
let V1{name} = data;
180+
```
181+
182+
Unpacking the enum value will abort if the variant is not the expected one. To ensure that all variants of an enum are handled, a `match` expression is recommended instead of a `let`. The `match` is checked at compile time, ensuring that all variants are covered. In some cases, tools like the Move Prover can be used to verify that unexpected aborts cannot happen with a `let`.
183+
184+
185+
## Enum Type Upgrade Compatibility
186+
187+
An enum type can be upgraded by another enum type if the new type only adds new variants at the end of the variant list. All variants present in the old enum type must also appear in the new type, in the same order and starting from the beginning. Consider the `VersionedData` type, which might have begun with a single version:
188+
189+
190+
```move
191+
enum VersionedData has key {
192+
V1{name: String}
193+
}
194+
```
195+
196+
This type could be upgraded to the version we used so far in this text:
197+
198+
```move
199+
enum VersionedData has key {
200+
V1{name: String}
201+
V2{name: String, age: u64}
202+
}
203+
```
204+
205+
That following upgrade would not be allowed, since the order of variants must be preserved:
206+
207+
```move
208+
enum VersionedData has key {
209+
V2{name: String, age: u64} // not a compatible upgrade
210+
V1{name: String}
211+
}
212+
```

apps/nextra/pages/en/build/smart-contracts/book/functions.mdx

+35-2
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,53 @@ script {
8080
}
8181
```
8282

83+
### `package` visibility
84+
85+
_Since Language Version 2.0_
86+
87+
A `package` function can be only called within the same package. The notion of a package is
88+
defined by the hosting environment of Move, and not explicit in the language. Typically, the package
89+
is defined by a manifest file `Move.toml` which is processed by the build environment.
90+
91+
The following works, provided the two modules belong to the same package and are at the same address:
92+
93+
```move
94+
module 0x42::m {
95+
package fun foo(): u64 { 0 }
96+
}
97+
98+
module 0x42::other {
99+
fun calls_m_foo(): u64 {
100+
0x42::m::foo() // valid
101+
}
102+
}
103+
```
104+
105+
An attempt to access `0x42::m::foo` from another package will fail at compile time.
106+
107+
In addition to the notation `package fun`, also the longer notation `public(package) fun` is supported.
108+
109+
Notice that package visibility is a compile time concept which is reduced by the compiler to friend visibility (described [below](#friend-visibility)), which can be verified by the Move VM. The Move VM guarantees that friend functions
110+
cannot be called across address boundaries, independent of what package system a compilation environment supports.
111+
112+
83113
#### `public(friend)` visibility
84114

115+
_Since Language Version 2.0_, `friend fun` replaces `public(friend) fun`. The old notation is still supported.
116+
85117
The `public(friend)` visibility modifier is a more restricted form of the `public` modifier to give more control about where a function can be used. A `public(friend)` function can be called by:
86118

87119
- other functions defined in the same module, or
88-
- functions defined in modules which are explicitly specified in the **friend list** (see [Friends](friends.mdx) on how to specify the friend list).
120+
- functions defined in modules which are explicitly specified in the **friend list** (see [Friends](friends.mdx) on how to specify the friend list), and which reside at the same address.
89121

90-
Note that since we cannot declare a script to be a friend of a module, the functions defined in scripts can never call a `public(friend)` function.
122+
Note that since we cannot declare a script to be a friend of a module, the functions defined in scripts can never call a `public(friend)` function.
91123

92124
```move
93125
address 0x42 {
94126
module m {
95127
friend 0x42::n; // friend declaration
96128
public(friend) fun foo(): u64 { 0 }
129+
friend fun foo2(): u64 { 0 } // Since Move 2.0
97130
98131
fun calls_foo(): u64 { foo() } // valid
99132
}

apps/nextra/pages/en/build/smart-contracts/book/global-storage-operators.mdx

+45-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Move programs can create, delete, and update [resources](structs-and-resources.m
1212

1313
Each of these instructions is parameterized by a type `T` with the [`key` ability](abilities.mdx). However, each type `T` _must be declared in the current module_. This ensures that a resource can only be manipulated via the API exposed by its defining module. The instructions also take either an [`address`](address.mdx) or [`&signer`](signer.mdx) representing the account address where the resource of type `T` is stored.
1414

15+
See also [index notation (`[]`)](#index-notation-for-storage-operators) for accessing global storage.
16+
17+
1518
## References to resources
1619

1720
References to global resources returned by `borrow_global` or `borrow_global_mut` mostly behave like references to local storage: they can be extended, read, and written using ordinary [reference operators](references.mdx) and passed as arguments to other function. However, there is one important difference between local and global references: **a function cannot return a reference that points into global storage**. For example, these two functions will each fail to compile:
@@ -30,7 +33,8 @@ module 0x42::example {
3033
}
3134
```
3235

33-
Move must enforce this restriction to guarantee absence of dangling references to global storage. [This section](#reference-safety-for-global-resources) contains much more detail for the interested reader.
36+
Move must enforce this restriction to guarantee absence of dangling references to global
37+
storage. [This section](#reference-safety-for-global-resources) contains much more detail for the interested reader.
3438

3539
## Global storage operators with generics
3640

@@ -243,3 +247,43 @@ address 0x42 {
243247
Line 16 acquires a reference to a global resource `m1::T`, then line 17 removes that same resource, which makes `t_ref` dangle. In this case, `acquires` annotations do not help us because the `borrow_then_remove_bad` function is outside the `m1` module that declares `T` (recall that `acquires` annotations can only be used for resources declared in the current module). Instead, the type system avoids this problem by preventing the return of a global reference at line 6.
244248

245249
Fancier type systems that would allow returning global references without sacrificing reference safety are possible, and we may consider them in future iterations of Move. We chose the current design because it strikes a good balance between being expressive, annotation burden, and type system complexity.
250+
251+
252+
## Index Notation for Storage Operators
253+
254+
_Since language version 2.0_
255+
256+
Instead of the verbose `borrow_global` and `borrow_global_mut` functions, one
257+
can also use index notations to access global storage.
258+
259+
The table below gives an overview of index notations for storage:
260+
261+
| Indexing Syntax | Storage Operation |
262+
|-------------------------|--------------------------------------------|
263+
| `&T[address]` | `borrow_global<T>(address)` |
264+
| `&mut T[address]` | `borrow_global_mut<T>(address)` |
265+
| `T[address]` | `*borrow_global<T>(address)` |
266+
| `T[address] = x` | `*borrow_global_mut<T>(address) = x` |
267+
| `&T[address].field` | `&borrow_global<T>(address).field` |
268+
| `&mut T[address].field` | `&mut borrow_global_mut<T>(address).field` |
269+
| `T[address].field` | `borrow_global<T>(address).field` |
270+
| `T[address].field = x` | `borrow_global_mut<T>(address).field = x` |
271+
272+
Here `T` represents a generic resource type that can take type parameters.
273+
274+
Notice that `T[address].field` fetches a reference to the resource from storage and then makes a copy of the field value (which must
275+
have the copy ability); it is a shortcut for `*&T[address].field`.
276+
277+
Examples:
278+
279+
```move
280+
struct R has key, drop { value: bool }
281+
282+
fun f1() acquires R {
283+
let x = &mut R[@0x1];
284+
x.value = false;
285+
assert!(R[@0x1].value == false);
286+
R[@0x1].value = true;
287+
assert!(R[@0x1].value == true);
288+
}
289+
```

apps/nextra/pages/en/build/smart-contracts/book/integers.mdx

+2
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ For example:
152152
- `(1 + 3 as u128)`
153153
- `(4/2 + 12345 as u256)`
154154

155+
Notice that since Language Version 2.0, casts don't always need to be in parentheses. Thus, `x as u8` is a valid expression.
156+
155157
## Ownership
156158

157159
As with the other scalar values built-in to the language, integer values are implicitly copyable, meaning they can be copied without an explicit instruction such as [`copy`](variables.mdx#move-and-copy).

0 commit comments

Comments
 (0)