diff --git a/book/src/before-we-begin/ide-support.md b/book/src/before-we-begin/ide-support.md index 40ce8ff3..5433c851 100644 --- a/book/src/before-we-begin/ide-support.md +++ b/book/src/before-we-begin/ide-support.md @@ -19,7 +19,7 @@ features. Whatever IDE you choose, you'll need to use the terminal to run the - [IntelliJ IDEA](https://www.jetbrains.com/idea/) is a commercial IDE from JetBrains. - [Move Language Plugin](https://plugins.jetbrains.com/plugin/14721-move-language) provides a Move - language extension for IntelliJ IDEA by [Pontem Network](https://pontem.network/). + on Sui language extension for IntelliJ IDEA by [MoveFuns](https://movefuns.org/). ## Emacs diff --git a/book/src/move-basics/abilities-introduction.md b/book/src/move-basics/abilities-introduction.md index 5c84cdbf..a6b5f89f 100644 --- a/book/src/move-basics/abilities-introduction.md +++ b/book/src/move-basics/abilities-introduction.md @@ -54,8 +54,8 @@ A struct without abilities cannot be discarded, or copied, or stored in the stor struct a _Hot Potato_. It is a joke, but it is also a good way to remember that a struct without abilities is like a hot potato - it can only be passed around and requires special handling. Hot Potato is one of the most powerful patterns in Move, we go in detail about it in the -[Hot Potato](./../programmability/hot-potato.md) chapter. +[Hot Potato Pattern](./../programmability/hot-potato-pattern.md) chapter. ## Further reading -- [Type Abilities](/reference/type-abilities.html) in the Move Reference. +- [Type Abilities](/reference/abilities.html) in the Move Reference. diff --git a/book/src/move-basics/assert-and-abort.md b/book/src/move-basics/assert-and-abort.md index 27aa0908..319dc4eb 100644 --- a/book/src/move-basics/assert-and-abort.md +++ b/book/src/move-basics/assert-and-abort.md @@ -45,7 +45,8 @@ The code above will, of course, abort with abort code `1`. The `assert!` macro is a built-in macro that can be used to assert a condition. If the condition is false, the transaction will abort with the given abort code. The `assert!` macro is a convenient way to abort a transaction if a condition is not met. The macro shortens the code otherwise written with -an `if` expression + `abort`. The `code` argument is required and has to be a `u64` value. +an `if` expression + `abort`. The `code` argument is optional, but has to be a `u64` value or an +`#[error]` (see below for more information). ```move {{#include ../../../packages/samples/sources/move-basics/assert-and-abort.move:assert}} @@ -63,6 +64,16 @@ code and make it easier to understand the abort scenarios. {{#include ../../../packages/samples/sources/move-basics/assert-and-abort.move:error_const}} ``` +## Error messages + +Move 2024 introduces a special type of error constant, marked with the `#[error]` attribute. This +attribute allows the error constant to be of type `vector` and can be used to store an error +message. + +```move +{{#include ../../../packages/samples/sources/move-basics/assert-and-abort.move:error_attribute}} +``` + ## Further reading - [Abort and Assert](/reference/abort-and-assert.html) in the Move Reference. diff --git a/book/src/move-basics/comments.md b/book/src/move-basics/comments.md index 5f7cabe5..64b76cd4 100644 --- a/book/src/move-basics/comments.md +++ b/book/src/move-basics/comments.md @@ -20,15 +20,11 @@ comment. ## Line comment -```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:line}} -``` - You can use double slash `//` to comment out the rest of the line. Everything after `//` will be ignored by the compiler. ```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:line_2}} +{{#include ../../../packages/samples/sources/move-basics/comments-line.move:main}} ``` ## Block comment @@ -38,7 +34,7 @@ Everything between `/*` and `*/` will be ignored by the compiler. You can use bl comment out a single line or multiple lines. You can even use them to comment out a part of a line. ```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:block}} +{{#include ../../../packages/samples/sources/move-basics/comments-block.move:main}} ``` This example is a bit extreme, but it shows how you can use block comments to comment out a part of @@ -51,7 +47,7 @@ They are similar to block comments, but they start with three slashes `///` and the definition of the item they document. ```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:doc}} +{{#include ../../../packages/samples/sources/move-basics/comments-doc.move:main}} ``` diff --git a/book/src/move-basics/constants.md b/book/src/move-basics/constants.md index f00e3bd6..b9eba314 100644 --- a/book/src/move-basics/constants.md +++ b/book/src/move-basics/constants.md @@ -24,7 +24,7 @@ price for a product, you might define a constant for it. Constants are stored in bytecode, and each time they are used, the value is copied. ```move -{{#include ../../../packages/samples/sources/move-basics/constants.move:shop_price}} +{{#include ../../../packages/samples/sources/move-basics/constants-shop-price.move:shop_price}} ``` ## Naming Convention @@ -35,7 +35,7 @@ It's a way to make constants stand out from other identifiers in the code. One e [error constants](./assert-and-abort.md#assert-and-abort), which are written in ECamelCase. ```move -{{#include ../../../packages/samples/sources/move-basics/constants.move:naming}} +{{#include ../../../packages/samples/sources/move-basics/constants-naming.move:naming}} ``` ## Constants are Immutable @@ -44,13 +44,13 @@ Constants can't be changed and assigned new values. They are part of the package inherently immutable. ```move -module book::immutable_constants { - const ITEM_PRICE: u64 = 100; +module book::immutable_constants; - // emits an error - fun change_price() { - ITEM_PRICE = 200; - } +const ITEM_PRICE: u64 = 100; + +// emits an error +fun change_price() { + ITEM_PRICE = 200; } ``` @@ -61,7 +61,7 @@ codebase. But due to constants being private to the module, they can't be access modules. One way to solve this is to define a "config" module that exports the constants. ```move -{{#include ../../../packages/samples/sources/move-basics/constants.move:config}} +{{#include ../../../packages/samples/sources/move-basics/constants-config.move:config}} ``` This way other modules can import and read the constants, and the update process is simplified. If diff --git a/book/src/move-basics/control-flow.md b/book/src/move-basics/control-flow.md index 627a6b43..4ddd3af7 100644 --- a/book/src/move-basics/control-flow.md +++ b/book/src/move-basics/control-flow.md @@ -27,7 +27,7 @@ control flow statements (explained in detail below): code - [`loop` and `while` loops](#repeating-statements-with-loops) - repeating a block of code - [`break` and `continue` statements](#exiting-a-loop-early) - exiting a loop early -- [`return`](#return) statement - exiting a function early +- [`return`](#early-return) statement - exiting a function early ## Conditional Statements diff --git a/book/src/move-basics/copy-ability.md b/book/src/move-basics/copy-ability.md index 9074f73a..b9f25fe5 100644 --- a/book/src/move-basics/copy-ability.md +++ b/book/src/move-basics/copy-ability.md @@ -54,4 +54,4 @@ All of the types defined in the standard library have the `copy` ability as well ## Further reading -- [Type Abilities](/reference/type-abilities.html) in the Move Reference. +- [Type Abilities](/reference/abilities.html) in the Move Reference. diff --git a/book/src/move-basics/drop-ability.md b/book/src/move-basics/drop-ability.md index ffe9f9c9..1ac0595b 100644 --- a/book/src/move-basics/drop-ability.md +++ b/book/src/move-basics/drop-ability.md @@ -56,7 +56,7 @@ properly handled and not ignored. A struct with a single `drop` ability is called a _Witness_. We explain the concept of a _Witness_ in the -[Witness and Abstract Implementation](./../programmability/witness-and-abstract-implementation.md) +[Witness and Abstract Implementation](./../programmability/witness-pattern.md) section. ## Types with the `drop` Ability @@ -76,4 +76,4 @@ All of the types defined in the standard library have the `drop` ability as well ## Further reading -- [Type Abilities](/reference/type-abilities.html) in the Move Reference. +- [Type Abilities](/reference/abilities.html) in the Move Reference. diff --git a/book/src/move-basics/function.md b/book/src/move-basics/function.md index 08c03f4c..308a91a8 100644 --- a/book/src/move-basics/function.md +++ b/book/src/move-basics/function.md @@ -38,7 +38,7 @@ function called `add` in the `math` module in the `book` package, the path to it `book::math::add`, or, if the module is imported, `math::add`. ```move -{{#include ../../../packages/samples/sources/move-basics/function.move:use_math}} +{{#include ../../../packages/samples/sources/move-basics/function_use.move:use_math}} ``` ## Multiple return values diff --git a/book/src/move-basics/importing-modules.md b/book/src/move-basics/importing-modules.md index 98695431..f20cbae3 100644 --- a/book/src/move-basics/importing-modules.md +++ b/book/src/move-basics/importing-modules.md @@ -38,7 +38,7 @@ Another module defined in the same package can import the first module using the ```move // File: sources/module_two.move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:module_two}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-two.move:module_two}} ``` ## Importing Members @@ -48,7 +48,7 @@ function or a single type from a module. The syntax is the same as for importing add the member name after the module path. ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:members}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-members.move:members}} ``` ## Grouping Imports @@ -58,7 +58,7 @@ when you need to import multiple members from the same module. Move allows group same module and from the same package. ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:grouped}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-grouped.move:grouped}} ``` Single function imports are less common in Move, since the function names can overlap and cause @@ -69,7 +69,7 @@ To import members and the module itself in the group import, you can use the `Se `Self` keyword refers to the module itself and can be used to import the module and its members. ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:self}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-self.move:self}} ``` ## Resolving Name Conflicts @@ -81,7 +81,7 @@ in different packages. To resolve the conflict and avoid ambiguity, Move offers rename the imported member. ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:conflict}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-conflict-resolution.move:conflict}} ``` ## Adding an External Dependency @@ -116,5 +116,5 @@ To import a module from another package, you use the `use` keyword followed by t module path consists of the package address (or alias) and the module name separated by `::`. ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:external}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-external.move:external}} ``` diff --git a/book/src/move-basics/module.md b/book/src/move-basics/module.md index ce7bc4a7..45b0f537 100644 --- a/book/src/move-basics/module.md +++ b/book/src/move-basics/module.md @@ -18,17 +18,19 @@ learn how to define a module, how to declare its members and how to access them ## Module declaration -Modules are declared using the `module` keyword followed by the package address, module name and the -module body inside the curly braces `{}`. The module name should be in `snake_case` - all lowercase -letters with underscores between words. Modules names must be unique in the package. +Modules are declared using the `module` keyword followed by the package address, module name, +semicolon, and the module body. The module name should be in `snake_case` - all lowercase letters +with underscores between words. Modules names must be unique in the package. Usually, a single file in the `sources/` folder contains a single module. The file name should match the module name - for example, a `donut_shop` module should be stored in the `donut_shop.move` file. You can read more about coding conventions in the -[Coding Conventions](../special-topics/coding-conventions.md) section. +[Coding Conventions](../guides/coding-conventions.md) section. + +> If you need to declare more than one module in a file, you must use [Module Block](#module-block). ```Move -{{#include ../../../packages/samples/sources/move-basics/module.move:module}} +{{#include ../../../packages/samples/sources/move-basics/module-label.move:module}} ``` Structs, functions, constants and imports all part of the module: @@ -63,6 +65,16 @@ book = "0x0" Module members are declared inside the module body. To illustrate that, let's define a simple module with a struct, a function and a constant: +```Move +{{#include ../../../packages/samples/sources/move-basics/module-members.move:members}} +``` + +## Module block + +Pre-2024 edition of Move required _module block_ - the contents of the module need to be surrounded +by curly braces `{}`. The main reason to use block and not _label_ is if you need to define more +than one module in a file. + ```Move {{#include ../../../packages/samples/sources/move-basics/module.move:members}} ``` diff --git a/book/src/move-basics/option.md b/book/src/move-basics/option.md index 05bb052d..e39d1fb4 100644 --- a/book/src/move-basics/option.md +++ b/book/src/move-basics/option.md @@ -4,8 +4,6 @@ Option is a type that represents an optional value which may or may not exist. T in Move is borrowed from Rust, and it is a very useful primitive in Move. `Option` is defined in the [Standard Library](./standard-library.md), and is defined as follows: -File: move-stdlib/source/option.move - ```move // File: move-stdlib/source/option.move /// Abstraction of a value that may or may not be present. diff --git a/book/src/move-basics/ownership-and-scope.md b/book/src/move-basics/ownership-and-scope.md index f11c4b18..94ce17af 100644 --- a/book/src/move-basics/ownership-and-scope.md +++ b/book/src/move-basics/ownership-and-scope.md @@ -20,21 +20,21 @@ scope and executes every expression and statement. Once the function scope end, defined in it are dropped or deallocated. ```move -module book::ownership { - public fun owner() { - let a = 1; // a is owned by the `owner` function - } // a is dropped here - - public fun other() { - let b = 2; // b is owned by the `other` function - } // b is dropped here - - #[test] - fun test_owner() { - owner(); - other(); - // a & b is not valid here - } +module book::ownership; + +public fun owner() { + let a = 1; // a is owned by the `owner` function +} // a is dropped here + +public fun other() { + let b = 2; // b is owned by the `other` function +} // b is dropped here + +#[test] +fun test_owner() { + owner(); + other(); + // a & b is not valid here } ``` @@ -48,18 +48,18 @@ If we changed the `owner` function to return the variable `a`, then the ownershi transferred to the caller of the function. ```move -module book::ownership { - public fun owner(): u8 { - let a = 1; // a defined here - a // scope ends, a is returned - } - - #[test] - fun test_owner() { - let a = owner(); - // a is valid here - } // a is dropped here +module book::ownership; + +public fun owner(): u8 { + let a = 1; // a defined here + a // scope ends, a is returned } + +#[test] +fun test_owner() { + let a = owner(); + // a is valid here +} // a is dropped here ``` ## Passing by Value @@ -69,22 +69,22 @@ transferred to this function. When performing this operation, we _move_ the valu another. This is also called _move semantics_. ```move -module book::ownership { - public fun owner(): u8 { - let a = 10; - a - } // a is returned - - public fun take_ownership(v: u8) { - // v is owned by `take_ownership` - } // v is dropped here - - #[test] - fun test_owner() { - let a = owner(); - take_ownership(a); - // a is not valid here - } +module book::ownership; + +public fun owner(): u8 { + let a = 10; + a +} // a is returned + +public fun take_ownership(v: u8) { + // v is owned by `take_ownership` +} // v is dropped here + +#[test] +fun test_owner() { + let a = owner(); + take_ownership(a); + // a is not valid here } ``` @@ -95,34 +95,34 @@ sequence of statements and expressions, and it has its own scope. Variables defi owned by this block, and when the block ends, the variables are dropped. ```move -module book::ownership { - public fun owner() { - let a = 1; // a is owned by the `owner` function's scope +module book::ownership; + +public fun owner() { + let a = 1; // a is owned by the `owner` function's scope + { + let b = 2; // b is owned by the block { - let b = 2; // b is owned by the block - { - let c = 3; // c is owned by the block - }; // c is dropped here - }; // b is dropped here - // a = b; // error: b is not valid here - // a = c; // error: c is not valid here - } // a is dropped here -} + let c = 3; // c is owned by the block + }; // c is dropped here + }; // b is dropped here + // a = b; // error: b is not valid here + // a = c; // error: c is not valid here +} // a is dropped here ``` However, shall we use the return value of a block, the ownership of the variable is transferred to the caller of the block. ```move -module book::ownership { - public fun owner(): u8 { - let a = 1; // a is owned by the `owner` function's scope - let b = { - let c = 2; // c is owned by the block - c // c is returned - }; // c is dropped here - a + b // both a and b are valid here - } +module book::ownership; + +public fun owner(): u8 { + let a = 1; // a is owned by the `owner` function's scope + let b = { + let c = 2; // c is owned by the block + c // c is returned + }; // c is dropped here + a + b // both a and b are valid here } ``` diff --git a/book/src/move-basics/references.md b/book/src/move-basics/references.md index 72b27c24..66ef6aa3 100644 --- a/book/src/move-basics/references.md +++ b/book/src/move-basics/references.md @@ -42,7 +42,6 @@ module book::metro_pass { {{#include ../../../packages/samples/sources/move-basics/references.move:header}} {{#include ../../../packages/samples/sources/move-basics/references.move:new}} -} ``` diff --git a/book/src/move-basics/standard-library.md b/book/src/move-basics/standard-library.md index da25e58a..d7ad52b6 100644 --- a/book/src/move-basics/standard-library.md +++ b/book/src/move-basics/standard-library.md @@ -22,16 +22,39 @@ and which module implements it. | [std::ascii](https://docs.sui.io/references/framework/move-stdlib/ascii) | Provides basic ASCII operations | [String](./string.md) | | [std::option](https://docs.sui.io/references/framework/move-stdlib/option) | Implements an `Option` | [Option](./option.md) | | [std::vector](https://docs.sui.io/references/framework/move-stdlib/vector) | Native operations on the vector type | [Vector](./vector.md) | -| [std::bcs](https://docs.sui.io/references/framework/move-stdlib/bcs) | Contains the `bcs::to_bytes()` function | [BCS](../move-basics/bcs.md) | +| [std::bcs](https://docs.sui.io/references/framework/move-stdlib/bcs) | Contains the `bcs::to_bytes()` function | [BCS](../programmability/bcs.md) | | [std::address](https://docs.sui.io/references/framework/move-stdlib/address) | Contains a single `address::length` function | [Address](./address.md) | | [std::type_name](https://docs.sui.io/references/framework/move-stdlib/type_name) | Allows runtime _type reflection_ | [Type Reflection](./type-reflection.md) | | std::hash | Hashing functions: `sha2_256` and `sha3_256` | [Cryptography and Hashing](../programmability/cryptography-and-hashing.md) | -| std::debug | Contains debugging functions, which are available in only in **test** mode | [Debugging](./debugging.md) | +| std::debug | Contains debugging functions, which are available in only in **test** mode | [Debugging](./guides/debugging.md) | | std::bit_vector | Provides operations on bit vectors | - | | std::fixed_point32 | Provides the `FixedPoint32` type | - | +## Integer Modules + +The Move Standard Library provides a set of functions associated with integer types. These functions +are split into multiple modules, each associated with a specific integer type. The modules should +not be imported directly, but their functions are available on every integer value. + +> All of the modules provide the same set of functions. Namely, `max`, `diff`, +> `divide_and_round_up`, `sqrt` and `pow`. + + +
+ +| Module | Description | +| ---------------------------------------------------------------------- | ----------------------------- | +| [std::u8](https://docs.sui.io/references/framework/move-stdlib/u8) | Functions for the `u8` type | +| [std::u16](https://docs.sui.io/references/framework/move-stdlib/u16) | Functions for the `u16` type | +| [std::u32](https://docs.sui.io/references/framework/move-stdlib/u32) | Functions for the `u32` type | +| [std::u64](https://docs.sui.io/references/framework/move-stdlib/u64) | Functions for the `u64` type | +| [std::u128](https://docs.sui.io/references/framework/move-stdlib/u128) | Functions for the `u128` type | +| [std::u256](https://docs.sui.io/references/framework/move-stdlib/u256) | Functions for the `u256` type | + +
+ ## Exported Addresses Standard Library exports one named address - `std = 0x1`. diff --git a/book/src/move-basics/struct-methods.md b/book/src/move-basics/struct-methods.md index f596314f..e35fee8f 100644 --- a/book/src/move-basics/struct-methods.md +++ b/book/src/move-basics/struct-methods.md @@ -41,7 +41,7 @@ with `hero_` and `villain_` respectively. However, we can create aliases for the they can be called on the instances of the structs without the prefix. ```move -{{#include ../../../packages/samples/sources/move-basics/struct-methods.move:hero_and_villain}} +{{#include ../../../packages/samples/sources/move-basics/struct-methods-2.move:hero_and_villain}} ``` As you can see, in the test function, we called the `health` method on the instances of `Hero` and @@ -57,7 +57,7 @@ associate it with the `Hero` struct. It will allow serializing the `Hero` struct bytes. ```move -{{#include ../../../packages/samples/sources/move-basics/struct-methods.move:hero_to_bytes}} +{{#include ../../../packages/samples/sources/move-basics/struct-methods-3.move:hero_to_bytes}} ``` ## Further reading diff --git a/book/src/move-basics/testing.md b/book/src/move-basics/testing.md index a66df80c..b49aa1ab 100644 --- a/book/src/move-basics/testing.md +++ b/book/src/move-basics/testing.md @@ -12,21 +12,21 @@ functions are regular functions, but they must take no arguments and have no ret are excluded from the bytecode, and are never published. ```move -module book::testing { - // Test attribute is placed before the `fun` keyword. Can be both above or - // right before the `fun` keyword: `#[test] fun my_test() { ... }` - // The name of the test would be `book::testing::simple_test`. - #[test] - fun simple_test() { - let sum = 2 + 2; - assert!(sum == 4, 1); - } - - // The name of the test would be `book::testing::more_advanced_test`. - #[test] fun more_advanced_test() { - let sum = 2 + 2 + 2; - assert!(sum == 4, 1); - } +module book::testing; + +// Test attribute is placed before the `fun` keyword. Can be both above or +// right before the `fun` keyword: `#[test] fun my_test() { ... }` +// The name of the test would be `book::testing::simple_test`. +#[test] +fun simple_test() { + let sum = 2 + 2; + assert!(sum == 4); +} + +// The name of the test would be `book::testing::more_advanced_test`. +#[test] fun more_advanced_test() { + let sum = 2 + 2 + 2; + assert!(sum == 4); } ``` @@ -60,21 +60,20 @@ fails. If the test fails with a different abort code, the test will fail. If the abort, the test will also fail. ```move -module book::testing_failure { +module book::testing_failure; - const EInvalidArgument: u64 = 1; +const EInvalidArgument: u64 = 1; - #[test] - #[expected_failure(abort_code = 0)] - fun test_fail() { - abort 0 // aborts with 0 - } +#[test] +#[expected_failure(abort_code = 0)] +fun test_fail() { + abort 0 // aborts with 0 +} - // attributes can be grouped together - #[test, expected_failure(abort_code = EInvalidArgument)] - fun test_fail_1() { - abort 1 // aborts with 1 - } +// attributes can be grouped together +#[test, expected_failure(abort_code = EInvalidArgument)] +fun test_fail_1() { + abort 1 // aborts with 1 } ``` @@ -89,29 +88,29 @@ important to remember that these functions should not be included in the final p where the `#[test_only]` attribute comes in handy. ```move -module book::testing { - // Public function which uses the `secret` function. - public fun multiply_by_secret(x: u64): u64 { - x * secret() - } - - /// Private function which is not available to the public. - fun secret(): u64 { 100 } - - #[test_only] - /// This function is only available for testing purposes in tests and other - /// test-only functions. Mind the visibility - for `#[test_only]` it is - /// common to use `public` visibility. - public fun secret_for_testing(): u64 { - secret() - } - - #[test] - // In the test environment we have access to the `secret_for_testing` function. - fun test_multiply_by_secret() { - let expected = secret_for_testing() * 2; - assert!(multiply_by_secret(2) == expected, 1); - } +module book::testing; + +// Public function which uses the `secret` function. +public fun multiply_by_secret(x: u64): u64 { + x * secret() +} + +/// Private function which is not available to the public. +fun secret(): u64 { 100 } + +#[test_only] +/// This function is only available for testing purposes in tests and other +/// test-only functions. Mind the visibility - for `#[test_only]` it is +/// common to use `public` visibility. +public fun secret_for_testing(): u64 { + secret() +} + +#[test] +// In the test environment we have access to the `secret_for_testing` function. +fun test_multiply_by_secret() { + let expected = secret_for_testing() * 2; + assert!(multiply_by_secret(2) == expected); } ``` diff --git a/book/src/move-basics/visibility.md b/book/src/move-basics/visibility.md index 8febf7fc..b01e69f9 100644 --- a/book/src/move-basics/visibility.md +++ b/book/src/move-basics/visibility.md @@ -12,14 +12,14 @@ A function or a struct defined in a module which has no visibility modifier is _ module. It can't be called from other modules. ```move -module book::internal_visibility { - // This function can be called from other functions in the same module - fun internal() { /* ... */ } - - // Same module -> can call internal() - fun call_internal() { - internal(); - } +module book::internal_visibility; + +// This function can be called from other functions in the same module +fun internal() { /* ... */ } + +// Same module -> can call internal() +fun call_internal() { + internal(); } ``` @@ -28,13 +28,13 @@ module book::internal_visibility { ```move -module book::try_calling_internal { - use book::internal_visibility; +module book::try_calling_internal; + +use book::internal_visibility; - // Different module -> can't call internal() - fun try_calling_internal() { - internal_visibility::internal(); - } +// Different module -> can't call internal() +fun try_calling_internal() { + internal_visibility::internal(); } ``` @@ -44,22 +44,22 @@ A struct or a function can be made _public_ by adding the `public` keyword befor `struct` keyword. ```move -module book::public_visibility { - // This function can be called from other modules - public fun public() { /* ... */ } -} +module book::public_visibility; + +// This function can be called from other modules +public fun public() { /* ... */ } ``` A public function can be imported and called from other modules. The following code will compile: ```move module book::try_calling_public { - use book::public_visibility; - // Different module -> can call public() - fun try_calling_public() { - public_visibility::public(); - } +use book::public_visibility; + +// Different module -> can call public() +fun try_calling_public() { + public_visibility::public(); } ``` @@ -69,20 +69,20 @@ Move 2024 introduces the _package visibility_ modifier. A function with _package called from any module within the same package. It can't be called from other packages. ```move -module book::package_visibility { - public(package) fun package_only() { /* ... */ } -} +module book::package_visibility; + +public(package) fun package_only() { /* ... */ } ``` A package function can be called from any module within the same package: ```move -module book::try_calling_package { - use book::package_visibility; +module book::try_calling_package; + +use book::package_visibility; - // Same package `book` -> can call package_only() - fun try_calling_package() { - package_visibility::package_only(); - } +// Same package `book` -> can call package_only() +fun try_calling_package() { + package_visibility::package_only(); } ``` diff --git a/book/src/programmability/capability.md b/book/src/programmability/capability.md index 1e93be4b..8c993937 100644 --- a/book/src/programmability/capability.md +++ b/book/src/programmability/capability.md @@ -26,7 +26,7 @@ A very common practice is to create a single `AdminCap` object on package publis application can have a setup phase where the admin account prepares the state of the application. ```move -{{#include ../../../packages/samples/sources/programmability/capability.move:admin_cap}} +{{#include ../../../packages/samples/sources/programmability/capability-2.move:admin_cap}} ``` ## Address check vs Capability @@ -40,13 +40,13 @@ Let's look at how the `new` function that creates a user would look like if it w check: ```move -{{#include ../../../packages/samples/sources/programmability/capability.move:with_address}} +{{#include ../../../packages/samples/sources/programmability/capability-3.move:with_address}} ``` And now, let's see how the same function would look like with the capability: ```move -{{#include ../../../packages/samples/sources/programmability/capability.move:with_capability}} +{{#include ../../../packages/samples/sources/programmability/capability-4.move:with_capability}} ``` Using capabilities has several advantages over the address check: diff --git a/book/src/programmability/collections.md b/book/src/programmability/collections.md index 34d26c4e..20979710 100644 --- a/book/src/programmability/collections.md +++ b/book/src/programmability/collections.md @@ -23,7 +23,7 @@ does not allow duplicate items. This makes it useful for storing a collection of as a list of unique IDs or addresses. ```move -{{#include ../../../packages/samples/sources/programmability/collections.move:vec_set}} +{{#include ../../../packages/samples/sources/programmability/collections-2.move:vec_set}} ``` VecSet will fail on attempt to insert an item that already exists in the set. @@ -40,7 +40,7 @@ to insert a key-value pair with a key that already exists in the map, the old va with the new value. ```move -{{#include ../../../packages/samples/sources/programmability/collections.move:vec_map}} +{{#include ../../../packages/samples/sources/programmability/collections-3.move:vec_map}} ``` ## Limitations @@ -59,7 +59,7 @@ results. > 'sui::vec_set::VecSet' may yield unexpected result_ ```move -{{#include ../../../packages/samples/sources/programmability/collections.move:vec_set_comparison}} +{{#include ../../../packages/samples/sources/programmability/collections-4.move:vec_set_comparison}} ``` In the example above, the comparison will fail because the order of insertion is not guaranteed, and diff --git a/book/src/programmability/events.md b/book/src/programmability/events.md index d2a78d36..68260d17 100644 --- a/book/src/programmability/events.md +++ b/book/src/programmability/events.md @@ -11,16 +11,16 @@ on-chain. Events are emitted by the `sui::event` module located in the ```move // File: sui-framework/sources/event.move -module sui::event { - /// Emit a custom Move event, sending the data offchain. - /// - /// Used for creating custom indexes and tracking onchain - /// activity in a way that suits a specific application the most. - /// - /// The type `T` is the main way to index the event, and can contain - /// phantom parameters, eg `emit(MyEvent)`. - public native fun emit(event: T); -} +module sui::event; + +/// Emit a custom Move event, sending the data offchain. +/// +/// Used for creating custom indexes and tracking onchain +/// activity in a way that suits a specific application the most. +/// +/// The type `T` is the main way to index the event, and can contain +/// phantom parameters, eg `emit(MyEvent)`. +public native fun emit(event: T); ``` ## Emitting Events diff --git a/book/src/programmability/hot-potato-pattern.md b/book/src/programmability/hot-potato-pattern.md index 82022d42..95fa1852 100644 --- a/book/src/programmability/hot-potato-pattern.md +++ b/book/src/programmability/hot-potato-pattern.md @@ -105,12 +105,13 @@ will cover in the next section. The pattern is used in various forms in the Sui Framework. Here are some examples: -- `sui::borrow` - uses hot potato to ensure that the borrowed value is returned to the correct - container. -- `sui::transfer_policy` - defines a `TransferRequest` - a hot potato which can only be consumed if - all conditions are met. -- `sui::token` - in the Closed Loop Token system, an `ActionRequest` carries the information about - the performed action and collects approvals similarly to `TransferRequest`. +- [sui::borrow](https://docs.sui.io/references/framework/sui-framework/borrow) - uses hot potato to + ensure that the borrowed value is returned to the correct container. +- [sui::transfer_policy](https://docs.sui.io/references/framework/sui-framework/transfer_policy) - + defines a `TransferRequest` - a hot potato which can only be consumed if all conditions are met. +- [sui::token](https://docs.sui.io/references/framework/sui-framework/token) - in the Closed Loop + Token system, an `ActionRequest` carries the information about the performed action and collects + approvals similarly to `TransferRequest`. ## Summary diff --git a/book/src/programmability/module-initializer.md b/book/src/programmability/module-initializer.md index 1ce6c2ce..76da5037 100644 --- a/book/src/programmability/module-initializer.md +++ b/book/src/programmability/module-initializer.md @@ -16,7 +16,7 @@ function will automatically be called when the module is published. In the same package, another module can have its own `init` function, encapsulating distinct logic. ```move -{{#include ../../../packages/samples/sources/programmability/module-initializer.move:other}} +{{#include ../../../packages/samples/sources/programmability/module-initializer-2.move:other}} ``` ## `init` features diff --git a/book/src/programmability/sui-framework.md b/book/src/programmability/sui-framework.md index bba2b2d8..a07182c6 100644 --- a/book/src/programmability/sui-framework.md +++ b/book/src/programmability/sui-framework.md @@ -47,9 +47,12 @@ still part of the same framework._
-| Module | Description | Chapter | -| ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------- | -| [sui::bcs](https://docs.sui.io/references/framework/sui-framework/bcs) | Implements the BCS encoding and decoding functions | [Binary Canonical Serialization](./bcs.md) | +| Module | Description | Chapter | +| ---------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------ | +| [sui::bcs](https://docs.sui.io/references/framework/sui-framework/bcs) | Implements the BCS encoding and decoding functions | [Binary Canonical Serialization](./bcs.md) | +| [sui::borrow](https://docs.sui.io/references/framework/sui-framework/borrow) | Implements the borrowing mechanic for borrowing by _value_ | [Hot Potato](./hot-potato-pattern.md) | +| [sui::hex](https://docs.sui.io/references/framework/sui-framework/hex) | Implements the hex encoding and decoding functions | - | +| [sui::types](https://docs.sui.io/references/framework/sui-framework/types) | Provides a way to check if the type is a One-Time-Witness | [One Time Witness](./one-time-witness.md) | ## Exported Addresses @@ -100,11 +103,12 @@ Commerce: - sui::transfer_policy -- sui::bcs -- sui::hex -- sui::math -- sui::types -- sui::borrow +Utilities: ++ sui::bcs ++ sui::hex +- sui::math (deprecated) ++ sui::types ++ sui::borrow - sui::authenticator diff --git a/book/src/programmability/witness-pattern.md b/book/src/programmability/witness-pattern.md index 30475ebf..a135d69f 100644 --- a/book/src/programmability/witness-pattern.md +++ b/book/src/programmability/witness-pattern.md @@ -19,14 +19,14 @@ to create a `Instance` instance. > the [Drop](./../move-basics/drop-ability.md) ability for the type. ```move -module book::witness { - /// A struct that requires a witness to be created. - public struct Instance { t: T } - - /// Create a new instance of `Instance` with the provided T. - public fun new(witness: T): Instance { - Instance { t: witness } - } +module book::witness; + +/// A struct that requires a witness to be created. +public struct Instance { t: T } + +/// Create a new instance of `Instance` with the provided T. +public fun new(witness: T): Instance { + Instance { t: witness } } ``` @@ -35,16 +35,16 @@ type `T`. This is a basic example of the witness pattern in Move. A module provi has a matching implementation, like the module `book::witness_source` below: ```move -module book::witness_source { - use book::witness::{Self, Instance}; +module book::witness_source; + +use book::witness::{Self, Instance}; - /// A struct used as a witness. - public struct W {} +/// A struct used as a witness. +public struct W {} - /// Create a new instance of `Instance`. - public fun new_instance(): Instance { - witness::new(W {}) - } +/// Create a new instance of `Instance`. +public fun new_instance(): Instance { + witness::new(W {}) } ``` diff --git a/book/src/storage/key-ability.md b/book/src/storage/key-ability.md index 3d1b0ea4..e8e707bb 100644 --- a/book/src/storage/key-ability.md +++ b/book/src/storage/key-ability.md @@ -59,4 +59,4 @@ we present the `sui::transfer` module, which provides native storage functions f ## Further reading -- [Type Abilities](/reference/type-abilities.html) in the Move Reference. +- [Type Abilities](/reference/abilities.html) in the Move Reference. diff --git a/book/src/storage/store-ability.md b/book/src/storage/store-ability.md index f4b0fdea..0d85bd9d 100644 --- a/book/src/storage/store-ability.md +++ b/book/src/storage/store-ability.md @@ -52,4 +52,4 @@ All of the types defined in the standard library have the `store` ability as wel ## Further reading -- [Type Abilities](/reference/type-abilities.html) in the Move Reference. +z- [Type Abilities](/reference/abilities.html) in the Move Reference. diff --git a/book/src/your-first-move/hello-world.md b/book/src/your-first-move/hello-world.md index eb3bbff6..5ed10068 100644 --- a/book/src/your-first-move/hello-world.md +++ b/book/src/your-first-move/hello-world.md @@ -83,9 +83,7 @@ _hello_world.move_ and the Move CLI has already placed commented out code inside ```move /* /// Module: hello_world -module hello_world::hello_world { - -} +module hello_world::hello_world; */ ``` @@ -116,21 +114,20 @@ The _hello_world_tests.move_ file contains a commented out test module template: ```move /* #[test_only] -module hello_world::hello_world_tests { - // uncomment this line to import the module - // use hello_world::hello_world; +module hello_world::hello_world_tests; +// uncomment this line to import the module +// use hello_world::hello_world; - const ENotImplemented: u64 = 0; +const ENotImplemented: u64 = 0; - #[test] - fun test_hello_world() { - // pass - } +#[test] +fun test_hello_world() { + // pass +} - #[test, expected_failure(abort_code = hello_world::hello_world_tests::ENotImplemented)] - fun test_hello_world_fail() { - abort ENotImplemented - } +#[test, expected_failure(abort_code = hello_world::hello_world_tests::ENotImplemented)] +fun test_hello_world_fail() { + abort ENotImplemented } */ ``` @@ -154,14 +151,14 @@ with the following: ```move /// The module `hello_world` under named address `hello_world`. /// The named address is set in the `Move.toml`. -module hello_world::hello_world { - // Imports the `String` type from the Standard Library - use std::string::String; - - /// Returns the "Hello, World!" as a `String`. - public fun hello_world(): String { - b"Hello, World!".to_string() - } +module hello_world::hello_world; + +// Imports the `String` type from the Standard Library +use std::string::String; + +/// Returns the "Hello, World!" as a `String`. +public fun hello_world(): String { + b"Hello, World!".to_string() } ``` @@ -203,13 +200,13 @@ Replace the contents of the `tests/hello_world_tests.move` with the following co ```move #[test_only] -module hello_world::hello_world_tests { - use hello_world::hello_world; +module hello_world::hello_world_tests; + +use hello_world::hello_world; - #[test] - fun test_hello_world() { - assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0); - } +#[test] +fun test_hello_world() { + assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0); } ``` diff --git a/book_zh/src/before-we-begin/ide-support.md b/book_zh/src/before-we-begin/ide-support.md index 981ef06d..bd417e3a 100644 --- a/book_zh/src/before-we-begin/ide-support.md +++ b/book_zh/src/before-we-begin/ide-support.md @@ -13,7 +13,7 @@ ## IntelliJ IDEA - [IntelliJ IDEA](https://www.jetbrains.com/idea/) 是 JetBrains 推出的一款商业 IDE。 -- [Move Language Plugin](https://plugins.jetbrains.com/plugin/14721-move-language) 是由 [Pontem Network](https://pontem.network/) 开发的 Move 语言扩展。 +- [Move Language Plugin](https://plugins.jetbrains.com/plugin/14721-move-language) 是由 [MoveFuns](https://movefuns.org/) 开发的 Sui Move 语言扩展。 ## Emacs diff --git a/book_zh/src/move-basics/abilities-introduction.md b/book_zh/src/move-basics/abilities-introduction.md index 974e78bf..423c1ec4 100644 --- a/book_zh/src/move-basics/abilities-introduction.md +++ b/book_zh/src/move-basics/abilities-introduction.md @@ -38,8 +38,8 @@ struct VeryAble has copy, drop { ## 没有能力 -没有能力的结构体不能被丢弃、复制或存储在存储中。我们称这种结构体为 _Hot Potato_。这是一个玩笑,但也是记住没有能力的结构体就像一个烫手山芋的好方法——它只能被传递,需要特殊处理。Hot Potato 是 Move 中最强大的模式之一,我们在[Hot Potato](./../programmability/hot-potato.md)章节中详细介绍了它。 +没有能力的结构体不能被丢弃、复制或存储在存储中。我们称这种结构体为 _Hot Potato_。这是一个玩笑,但也是记住没有能力的结构体就像一个烫手山芋的好方法——它只能被传递,需要特殊处理。Hot Potato 是 Move 中最强大的模式之一,我们在[Hot Potato 模式(烫手山芋模式)](./../programmability/hot-potato.md)章节中详细介绍了它。 ## 延伸阅读 -- Move参考中的[类型能力](/reference/type-abilities.html)。 \ No newline at end of file +- Move参考中的[类型能力](/reference/abilities.html)。 \ No newline at end of file diff --git a/book_zh/src/move-basics/assert-and-abort.md b/book_zh/src/move-basics/assert-and-abort.md index 2553aebf..4becd707 100644 --- a/book_zh/src/move-basics/assert-and-abort.md +++ b/book_zh/src/move-basics/assert-and-abort.md @@ -16,20 +16,29 @@ ## 断言 (assert!) -`assert!` 是一个内置宏,用于断言一个条件是否成立。如果条件为假,则交易会中止,并返回给定的中止代码。`assert!` 宏提供了一种方便的方法,可以在条件不满足时中止交易。该宏可以替代使用 `if` 语句和 `abort` 来编写的代码。`code` 参数是必需的,它必须是一个 `u64` 值。 +`assert!` 是一个内置宏,用于断言一个条件是否成立。如果条件为假,则交易会中止,并返回给定的中止代码。这个宏简化了原本需要使用 `if` 表达式和 `abort` 编写的代码。`code` 参数是可选的,但必须是一个 `u64` 值或 `#[error]`(详细信息请参见下文)。 ```move {{#include ../../../packages/samples/sources/move-basics/assert-and-abort.move:assert}} ``` -## 错误代码 +## 错误常量 -为了使中止代码更具描述性,定义 [错误代码](./constants.md) 是一个好习惯。错误代码使用 `const` 关键字声明,通常以 `E` 开头,后面跟驼峰式命名。错误代码与其他常量没有什么不同,没有特殊的处理方式,但是它们可以提高代码的可读性,并使人们更容易理解中止场景。 +为了使中止代码更具描述性,定义 [错误常量](./constants.md) 是一个好习惯。错误代码使用 `const` 关键字声明,通常以 `E` 开头,后面跟驼峰式命名。错误代码与其他常量没有什么不同,没有特殊的处理方式,但是它们可以提高代码的可读性,并使人们更容易理解中止场景。 ```move {{#include ../../../packages/samples/sources/move-basics/assert-and-abort.move:error_const}} ``` +## 错误消息 + +Move 2024 引入了一种特殊的错误常量类型,用 `#[error]` 属性标记。 这个属性允许错误常量的类型为 `vector`,并可用于存储错误消息。 + +```move +{{#include ../../../packages/samples/sources/move-basics/assert-and-abort.move:error_attribute}} +``` + + ## 进一步阅读 * Move 参考手册中的 [终止和断言](/reference/abort-and-assert.html)。 diff --git a/book_zh/src/move-basics/comments.md b/book_zh/src/move-basics/comments.md index 2e808b70..f5b10e1f 100644 --- a/book_zh/src/move-basics/comments.md +++ b/book_zh/src/move-basics/comments.md @@ -16,14 +16,10 @@ ## 行注释 -```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:line}} -``` - 你可以使用双斜杠 `//` 来注释掉余下的行。编译器会忽略 `//` 之后的所有内容。 ```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:line_2}} +{{#include ../../../packages/samples/sources/move-basics/comments-line.move:main}} ``` ## 块注释 @@ -31,7 +27,7 @@ 块注释用于注释掉一段代码。它们以 `/*` 开始,以 `*/` 结束。编译器会忽略 `/*` 和 `*/` 之间的所有内容。你可以使用块注释来注释掉单行或多行代码,甚至可以注释掉一行中的一部分。 ```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:block}} +{{#include ../../../packages/samples/sources/move-basics/comments-block.move:main}} ``` 这个例子有点极端,但它展示了如何使用块注释来注释掉一行中的一部分。 @@ -41,7 +37,7 @@ 文档注释是一种特殊的注释,用于为代码生成文档。它们类似于块注释,但以三个斜杠 `///` 开始,并放在它们所记录的项目定义之前。 ```Move -{{#include ../../../packages/samples/sources/move-basics/comments.move:doc}} +{{#include ../../../packages/samples/sources/move-basics/comments-doc.move:main}} ``` diff --git a/book_zh/src/move-basics/constants.md b/book_zh/src/move-basics/constants.md index 9844b951..dd815405 100644 --- a/book_zh/src/move-basics/constants.md +++ b/book_zh/src/move-basics/constants.md @@ -21,7 +21,7 @@ Links: 常量是在模块级别定义的不可变值。它们通常用作给模块中使用的静态值命名的一种方式。例如,如果有一个产品的默认价格,可以为其定义一个常量。常量存储在模块的字节码中,每次使用时,值都会被复制。 ```move -{{#include ../../../packages/samples/sources/move-basics/constants.move:shop_price}} +{{#include ../../../packages/samples/sources/move-basics/constants-shop-price.move:shop_price}} ``` ## 命名约定 @@ -29,7 +29,7 @@ Links: 常量必须以大写字母开头 - 这在编译器级别是强制执行的。对于用作值的常量,有一个约定,使用大写字母和下划线来分隔单词。这是一种让常量在代码中突出显示的方式。一个例外是[错误常量](./assert-and-abort.md#assert-and-abort),它们使用ECamelCase编写。 ```move -{{#include ../../../packages/samples/sources/move-basics/constants.move:naming}} +{{#include ../../../packages/samples/sources/move-basics/constants-naming.move:naming}} ``` ## 常量是不可变的 @@ -37,13 +37,13 @@ Links: 常量无法更改或赋予新值。它们是包字节码的一部分,并且是固有的不可变的。 ```move -module book::immutable_constants { - const ITEM_PRICE: u64 = 100; +module book::immutable_constants; - // 会产生错误 - fun change_price() { - ITEM_PRICE = 200; - } +const ITEM_PRICE: u64 = 100; + +// emits an error +fun change_price() { + ITEM_PRICE = 200; } ``` @@ -52,7 +52,7 @@ module book::immutable_constants { 应用程序的常见用例是定义一组在整个代码库中使用的常量。但是由于常量是模块的私有内容,无法从其他模块中访问。解决这个问题的方法之一是定义一个"config"模块,导出常量。 ```move -{{#include ../../../packages/samples/sources/move-basics/constants.move:config}} +{{#include ../../../packages/samples/sources/move-basics/constants-config.move:config}} ``` 这样,其他模块可以导入和读取常量,并简化更新过程。如果需要更改常量,只需要在包升级期间更新配置模块即可。 diff --git a/book_zh/src/move-basics/control-flow.md b/book_zh/src/move-basics/control-flow.md index 2d634939..8c6caf84 100644 --- a/book_zh/src/move-basics/control-flow.md +++ b/book_zh/src/move-basics/control-flow.md @@ -5,7 +5,7 @@ - [`if` 和 `if-else`](#条件语句) - 根据条件决定是否执行一段代码 - [`loop` 和 `while` 循环](#使用循环重复语句) - 重复执行一段代码 - [`break` 和 `continue` 语句](#提前退出循环) - 提前退出循环 -- [`return` 语句](#return) - 提前退出函数 +- [`return` 语句](#early-return) - 提前退出函数 ## 条件语句 diff --git a/book_zh/src/move-basics/copy-ability.md b/book_zh/src/move-basics/copy-ability.md index ae7b183f..d6013962 100644 --- a/book_zh/src/move-basics/copy-ability.md +++ b/book_zh/src/move-basics/copy-ability.md @@ -43,4 +43,4 @@ Move 中的所有本机类型都具有 `copy` 能力。这包括: ## 进一步阅读 -- [类型能力](/reference/type-abilities.html) 在 Move 参考中。 \ No newline at end of file +- [类型能力](/reference/abilities.html) 在 Move 参考中。 \ No newline at end of file diff --git a/book_zh/src/move-basics/drop-ability.md b/book_zh/src/move-basics/drop-ability.md index 060f54f0..13c96483 100644 --- a/book_zh/src/move-basics/drop-ability.md +++ b/book_zh/src/move-basics/drop-ability.md @@ -46,7 +46,7 @@ Links: `drop` 能力通常在自定义的集合类型上使用,以消除在不再需要集合时的特殊处理需求。例如,`vector` 类型具有 `drop` 能力,这使得在不再需要时可以忽略该向量。然而,Move类型系统最大的特点是能够没有 `drop`。这确保了资产得到正确处理,而不被忽略。 -一个仅具有 `drop` 能力的结构体称为 _Witness_。我们在[见证和抽象实现](./../programmability/witness-and-abstract-implementation.md)部分解释了 _Witness_ 的概念。 +一个仅具有 `drop` 能力的结构体称为 _Witness_。我们在[见证和抽象实现](./../programmability/witness-pattern.md)部分解释了 _Witness_ 的概念。 ## 带有 `drop` 能力的类型 @@ -65,4 +65,4 @@ Move中的所有原生类型都具有 `drop` 能力。包括: ## 进一步阅读 -- Move参考中的[Type Abilities](/reference/type-abilities.html)。 \ No newline at end of file +- Move参考中的[Type Abilities](/reference/abilities.html)。 \ No newline at end of file diff --git a/book_zh/src/move-basics/function.md b/book_zh/src/move-basics/function.md index 48cb07fe..5efb9b8a 100644 --- a/book_zh/src/move-basics/function.md +++ b/book_zh/src/move-basics/function.md @@ -21,7 +21,7 @@ 与任何其他模块成员一样,函数可以通过路径导入和访问。路径由模块路径和函数名称组成,用 `::` 分隔。例如,如果在 `book` 包的 `math` 模块中有一个名为 `add` 的函数,则其路径为 `book::math::add`;如果模块已导入,则为 `math::add`。 ```move -{{#include ../../../packages/samples/sources/move-basics/function.move:use_math}} +{{#include ../../../packages/samples/sources/move-basics/function_use.move:use_math}} ``` ## 多返回值 diff --git a/book_zh/src/move-basics/importing-modules.md b/book_zh/src/move-basics/importing-modules.md index 8de236a4..da541c0d 100644 --- a/book_zh/src/move-basics/importing-modules.md +++ b/book_zh/src/move-basics/importing-modules.md @@ -15,7 +15,7 @@ Move 通过允许模块导入来实现高模块化和代码重用。同一个包 ```move // 文件: sources/module_two.move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:module_two}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-two.move:module_two}} ``` ## 导入成员 @@ -23,7 +23,7 @@ Move 通过允许模块导入来实现高模块化和代码重用。同一个包 您还可以从模块中导入特定成员。这在您只需要来自模块的单个函数或单个类型时非常有用。语法与导入模块相同,但您在模块路径之后添加成员名称。 ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:members}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-members.move:members}} ``` ## 分组导入 @@ -31,7 +31,7 @@ Move 通过允许模块导入来实现高模块化和代码重用。同一个包 可以使用花括号 `{}` 将导入分组到单个 `use` 语句中。当您需要从同一个模块导入多个成员时,这非常有用。Move 允许对来自同一个模块和来自同一个包的导入进行分组。 ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:grouped}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-grouped.move:grouped}} ``` 在 Move 中,单个函数的导入并不常见,因为函数名称可能会重叠并引起混淆。推荐的做法是导入整个模块并使用模块路径来访问函数。类型具有唯一名称,应该单独导入。 @@ -39,7 +39,7 @@ Move 通过允许模块导入来实现高模块化和代码重用。同一个包 要将成员和模块本身一起导入到组导入中,可以使用 `Self` 关键字。`Self` 关键字指的是模块本身,可用于导入模块及其成员。 ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:self}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-self.move:self}} ``` ## 解决命名冲突 @@ -47,7 +47,7 @@ Move 通过允许模块导入来实现高模块化和代码重用。同一个包 从不同模块导入多个成员时,可能会出现命名冲突。例如,如果您导入了两个都具有相同名称的函数的模块,则需要使用模块路径来访问该函数。不同的包中也可能存在具有相同名称的模块。为了解决冲突并避免歧义,Move 提供了 `as` 关键字来重命名导入的成员。 ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:conflict}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-conflict-resolution.move:conflict}} ``` ## 添加外部依赖项 @@ -71,5 +71,5 @@ Local = { local = "../my_other_package" } 要从另一个包导入模块,请使用 `use` 关键字后跟模块路径。模块路径由包地址(或别名)和模块名称组成,两者之间用 `::` 分隔。 ```move -{{#include ../../../packages/samples/sources/move-basics/importing-modules.move:external}} +{{#include ../../../packages/samples/sources/move-basics/importing-modules-external.move:external}} ``` \ No newline at end of file diff --git a/book_zh/src/move-basics/module.md b/book_zh/src/move-basics/module.md index 06cbfba3..968567ba 100644 --- a/book_zh/src/move-basics/module.md +++ b/book_zh/src/move-basics/module.md @@ -2,12 +2,14 @@ ## 模块声明 -使用 `module` 关键字后跟包地址、模块名称和模块体在花括号 `{}` 内来声明模块。模块名称应采用 `snake_case` 形式,即所有小写字母,单词之间用下划线分隔。模块名称在包内必须是唯一的。 +模块使用 `module` 关键字声明,后跟包地址、模块名称、分号以及模块主体。模块名称应采用 `snake_case` 命名规则,即全部使用小写字母,单词之间用下划线分隔。模块名称在包内必须唯一。 -通常,`sources/` 文件夹中的单个文件包含一个模块。文件名应与模块名称匹配 - 例如,`donut_shop` 模块应存储在 `donut_shop.move` 文件中。您可以在[Coding Conventions](../special-topics/coding-conventions.md)部分了解更多有关编码约定的信息。 +通常,`sources/` 文件夹中的单个文件包含一个模块。文件名应与模块名称匹配 - 例如,`donut_shop` 模块应存储在 `donut_shop.move` 文件中。您可以在[Coding Conventions](../guides/coding-conventions.md)部分了解更多有关编码约定的信息。 + +> 如果需要在一个文件中声明多个模块,必须使用 [模块块](#模块块)。 ```move -{{#include ../../../packages/samples/sources/move-basics/module.move:module}} +{{#include ../../../packages/samples/sources/move-basics/module-label.move:module}} ``` 模块的成员包括结构体、函数、常量和导入: @@ -38,10 +40,19 @@ book = "0x0" 模块成员声明在模块体内部。为了说明这一点,让我们定义一个简单的模块,其中包含一个结构体、一个函数和一个常量: -```move +```Move +{{#include ../../../packages/samples/sources/move-basics/module-members.move:members}} +``` + +## 模块块 + +在 2024 年之前的 Move 版本中,需要使用**模块块**——模块的内容必须用大括号 `{}` 包裹。使用模块块而不是**标签**的主要原因是当需要在一个文件中定义多个模块时。 + +```Move {{#include ../../../packages/samples/sources/move-basics/module.move:members}} ``` + ## 进一步阅读 - 在 Move 参考文档中阅读有关[模块](/reference/modules.html)的更多信息。 \ No newline at end of file diff --git a/book_zh/src/move-basics/option.md b/book_zh/src/move-basics/option.md index 1da92456..9cfe1152 100644 --- a/book_zh/src/move-basics/option.md +++ b/book_zh/src/move-basics/option.md @@ -2,7 +2,6 @@ Option 是一种表示可选值的类型,它可能存在,也可能不存在。Move 中的 Option 概念借鉴自 Rust,它是 Move 中非常有用的原语。`Option` 在[标准库](./standard-library.md)中定义,如下所示: -文件:move-stdlib/source/option.move ```move // 文件:move-stdlib/source/option.move diff --git a/book_zh/src/move-basics/ownership-and-scope.md b/book_zh/src/move-basics/ownership-and-scope.md index 3074fcf3..af7ea254 100644 --- a/book_zh/src/move-basics/ownership-and-scope.md +++ b/book_zh/src/move-basics/ownership-and-scope.md @@ -7,21 +7,19 @@ Move 中的每个变量都拥有一个作用域和一个所有者。作用域是 在函数作用域中定义的变量由该作用域所有。运行时会遍历函数作用域并执行每个表达式和语句。一旦函数作用域结束,定义在其中的变量就会被丢弃或释放。 ```move -module book::ownership { - public fun owner() { - let a = 1; // a 由 `owner` 函数拥有 - } // a 在此处被丢弃 - - public fun other() { - let b = 2; // b 由 `other` 函数拥有 - } // b 在此处被丢弃 - - #[test] - fun test_owner() { - owner(); - other(); - // a 和 b 在此处无效 - } +module book::ownership; + +public fun owner() { + let a = 1; // a is owned by the `owner` function +} // a is dropped here +public fun other() { + let b = 2; // b is owned by the `other` function +} // b is dropped here +#[test] +fun test_owner() { + owner(); + other(); + // a & b is not valid here } ``` @@ -32,18 +30,18 @@ module book::ownership { 如果我们将 `owner` 函数改为返回变量 `a`,那么 `a` 的所有权将被转移到函数的调用者。 ```move -module book::ownership { - public fun owner(): u8 { - let a = 1; // a 在此处定义 - a // 作用域结束,a 被返回 - } - - #[test] - fun test_owner() { - let a = owner(); - // a 在此处有效 - } // a 在此处被丢弃 +module book::ownership; + +public fun owner(): u8 { + let a = 1; // a defined here + a // scope ends, a is returned } + +#[test] +fun test_owner() { + let a = owner(); + // a is valid here +} // a is dropped here ``` ## 按值传递 @@ -51,22 +49,22 @@ module book::ownership { 此外,如果我们将变量 `a` 传递给另一个函数,则 `a` 的所有权将被转移到该函数。执行此操作时,我们将值从一个作用域 _移动_ 到另一个作用域。这也被称为 _move 语义_。 ```move -module book::ownership { - public fun owner(): u8 { - let a = 10; - a - } // a 被返回 - - public fun take_ownership(v: u8) { - // v 由 `take_ownership` 拥有 - } // v 在此处被丢弃 - - #[test] - fun test_owner() { - let a = owner(); - take_ownership(a); - // a 在此处无效 - } +module book::ownership; + +public fun owner(): u8 { + let a = 10; + a +} // a is returned + +public fun take_ownership(v: u8) { + // v is owned by `take_ownership` +} // v is dropped here + +#[test] +fun test_owner() { + let a = owner(); + take_ownership(a); + // a is not valid here } ``` @@ -75,33 +73,33 @@ module book::ownership { 每个函数都有一个主作用域,还可以通过使用块来拥有子作用域。块是一系列语句和表达式,它有自己的作用域。在块中定义的变量由该块拥有,当块结束时,变量将被丢弃。 ```move -module book::ownership { - public fun owner() { - let a = 1; // a 由 `owner` 函数的作用域拥有 +module book::ownership; + +public fun owner() { + let a = 1; // a is owned by the `owner` function's scope + { + let b = 2; // b is owned by the block { - let b = 2; // b 由块拥有 - { - let c = 3; // c 由块拥有 - }; // c 在此处被丢弃 - }; // b 在此处被丢弃 - // a = b; // 错误:b 在此处无效 - // a = c; // 错误:c 在此处无效 - } // a 在此处被丢弃 -} + let c = 3; // c is owned by the block + }; // c is dropped here + }; // b is dropped here + // a = b; // error: b is not valid here + // a = c; // error: c is not valid here +} // a is dropped here ``` 但是,如果我们使用块的返回值,则变量的所有权将被转移到块的调用者。 ```move -module book::ownership { - public fun owner(): u8 { - let a = 1; // a 由 `owner` 函数的作用域拥有 - let b = { - let c = 2; // c 由块拥有 - c // c 被返回 - }; // c 在此处被丢弃 - a + b // a 和 b 在此处都有效 - } +module book::ownership; + +public fun owner(): u8 { + let a = 1; // a is owned by the `owner` function's scope + let b = { + let c = 2; // c is owned by the block + c // c is returned + }; // c is dropped here + a + b // both a and b are valid here } ``` diff --git a/book_zh/src/move-basics/standard-library.md b/book_zh/src/move-basics/standard-library.md index 423d43c1..4ad389fb 100644 --- a/book_zh/src/move-basics/standard-library.md +++ b/book_zh/src/move-basics/standard-library.md @@ -14,16 +14,36 @@ Move 标准库提供了用于本机类型和操作的功能集合。它是一组 | [std::ascii](https://docs.sui.io/references/framework/move-stdlib/ascii) | 提供基本的 ASCII 操作 | [字符串](./string.md) | | [std::option](https://docs.sui.io/references/framework/move-stdlib/option) | 实现 `Option` 类型 | [Option](./option.md) | | [std::vector](https://docs.sui.io/references/framework/move-stdlib/vector) | 对向量类型进行本地操作 | [Vector](./vector.md) | -| [std::bcs](https://docs.sui.io/references/framework/move-stdlib/bcs) | 包含 `bcs::to_bytes()` 函数 | [BCS](../move-basics/bcs.md) | +| [std::bcs](https://docs.sui.io/references/framework/move-stdlib/bcs) | 包含 `bcs::to_bytes()` 函数 | [BCS](../programmability/bcs.md) | | [std::address](https://docs.sui.io/references/framework/move-stdlib/address) | 包含单一的 `address::length` 函数 | [地址](./address.md) | | [std::type_name](https://docs.sui.io/references/framework/move-stdlib/type_name) | 允许运行时类型反射 | [类型反射](./type-reflection.md) | | std::hash | 哈希函数:`sha2_256` 和 `sha3_256` | [加密和哈希](../programmability/cryptography-and-hashing.md) | -| std::debug | 包含调试函数,仅在 **测试** 模式下可用 | [调试](./debugging.md) | +| std::debug | 包含调试函数,仅在 **测试** 模式下可用 | [调试](./guides/debugging.md) | | std::bit_vector | 提供位向量操作 | - | | std::fixed_point32 | 提供 `FixedPoint32` 类型 | - |
+## 整数模块 + +Move 标准库提供了一组与整数类型相关的函数。这些函数被分为多个模块,每个模块与特定的整数类型相关联。这些模块无需直接导入,因为它们的函数可用于每个整数值。 + +> 所有模块都提供相同的一组函数,包括 `max`、`diff`、`divide_and_round_up`、`sqrt` 和 `pow`。 + + +
+ +| 模块 | 描述 | +| ---------------------------------------------------------------------- | ---------------------------- | +| [std::u8](https://docs.sui.io/references/framework/move-stdlib/u8) | `u8` 类型的函数 | +| [std::u16](https://docs.sui.io/references/framework/move-stdlib/u16) | `u16` 类型的函数 | +| [std::u32](https://docs.sui.io/references/framework/move-stdlib/u32) | `u32` 类型的函数 | +| [std::u64](https://docs.sui.io/references/framework/move-stdlib/u64) | `u64` 类型的函数 | +| [std::u128](https://docs.sui.io/references/framework/move-stdlib/u128) | `u128` 类型的函数 | +| [std::u256](https://docs.sui.io/references/framework/move-stdlib/u256) | `u256` 类型的函数 | + +
+ ## 导出的地址 标准库导出了一个命名地址 - `std = 0x1`。 diff --git a/book_zh/src/move-basics/struct-methods.md b/book_zh/src/move-basics/struct-methods.md index 084c490c..e839aecb 100644 --- a/book_zh/src/move-basics/struct-methods.md +++ b/book_zh/src/move-basics/struct-methods.md @@ -31,7 +31,7 @@ public use fun function_path as Type.method_name; 在下面的示例中,我们更改了 `hero` 模块,并添加了另一种类型 - `Villain`。`Hero` 和 `Villain` 都具有类似的字段名称和方法。为了避免名称冲突,我们为这些方法添加了前缀 `hero_` 和 `villain_`。但是,我们可以为这些方法创建别名,以便在结构体实例上调用时不需要前缀。 ```move -{{#include ../../../packages/samples/sources/move-basics/struct-methods.move:hero_and_villain}} +{{#include ../../../packages/samples/sources/move-basics/struct-methods-2.move:hero_and_villain}} ``` 正如你所看到的,在测试函数中,我们在 `Hero` 和 `Villain` 的实例上调用了 `health` 方法,而不使用前缀。编译器将自动将方法与结构体关联起来。 @@ -41,7 +41,7 @@ public use fun function_path as Type.method_name; 还可以将在另一个模块中定义的函数与当前模块的结构体关联起来。按照相同的方法,我们可以为在另一个模块中定义的方法创建别名。让我们使用[标准库](./standard-library.md)中的 `bcs::to_bytes` 方法,并将其与 `Hero` 结构体关联起来。这将允许将 `Hero` 结构体序列化为字节向量。 ```move -{{#include ../../../packages/samples/sources/move-basics/struct-methods.move:hero_to_bytes}} +{{#include ../../../packages/samples/sources/move-basics/struct-methods-3.move:hero_to_bytes}} ``` ## 进一步阅读 diff --git a/book_zh/src/move-basics/testing.md b/book_zh/src/move-basics/testing.md index 27ce31cb..d6a88948 100644 --- a/book_zh/src/move-basics/testing.md +++ b/book_zh/src/move-basics/testing.md @@ -10,21 +10,21 @@ 测试函数是常规函数,但不能接受任何参数,也不能有返回值。它们不会被编译成字节码,也不会被发布。 ```move -module book::testing { - // `#[test]` 属性放置在 `fun` 关键字之前。 - // 可以放在函数签名的上方或紧挨着 `fun` 关键字:`#[test] fun my_test() { ... }` - // 测试的名称将会是 `book::testing::simple_test`。 - #[test] - fun simple_test() { - let sum = 2 + 2; - assert!(sum == 4, 1); - } - - // 测试的名称将会是 `book::testing::more_advanced_test`. - #[test] fun more_advanced_test() { - let sum = 2 + 2 + 2; - assert!(sum == 4, 1); - } +module book::testing; + +// Test attribute is placed before the `fun` keyword. Can be both above or +// right before the `fun` keyword: `#[test] fun my_test() { ... }` +// The name of the test would be `book::testing::simple_test`. +#[test] +fun simple_test() { + let sum = 2 + 2; + assert!(sum == 4); +} + +// The name of the test would be `book::testing::more_advanced_test`. +#[test] fun more_advanced_test() { + let sum = 2 + 2 + 2; + assert!(sum == 4); } ``` @@ -58,21 +58,20 @@ $ sui move test 如果执行过程中没有终止,测试同样会失败。 ```move -module book::testing_failure { +module book::testing_failure; - const EInvalidArgument: u64 = 1; +const EInvalidArgument: u64 = 1; - #[test] - #[expected_failure(abort_code = 0)] - fun test_fail() { - abort 0 // 以终止码 0 终止 - } +#[test] +#[expected_failure(abort_code = 0)] +fun test_fail() { + abort 0 // aborts with 0 +} - // 属性可以组合在一起使用。 - #[test, expected_failure(abort_code = EInvalidArgument)] - fun test_fail_1() { - abort 1 // 以终止码 1 终止 - } +// attributes can be grouped together +#[test, expected_failure(abort_code = EInvalidArgument)] +fun test_fail_1() { + abort 1 // aborts with 1 } ``` @@ -87,29 +86,29 @@ module book::testing_failure { 这时,`#[test_only]` 属性就派上用场了。 ```move -module book::testing { - // 使用 `secret` 函数的公共函数 - public fun multiply_by_secret(x: u64): u64 { - x * secret() - } - - /// 私有函数不能被公共函数使用 - fun secret(): u64 { 100 } - - #[test_only] - /// 此函数仅用于测试中的测试目的以及其他仅限测试的函数。 - /// 注意可见性——对于 `#[test_only]`, - /// 通常使用 `public` 可见性。 - public fun secret_for_testing(): u64 { - secret() - } - - #[test] - // 在测试环境中,我们可以访问 `secret_for_testing` 函数。 - fun test_multiply_by_secret() { - let expected = secret_for_testing() * 2; - assert!(multiply_by_secret(2) == expected, 1); - } +module book::testing; + +// Public function which uses the `secret` function. +public fun multiply_by_secret(x: u64): u64 { + x * secret() +} + +/// Private function which is not available to the public. +fun secret(): u64 { 100 } + +#[test_only] +/// This function is only available for testing purposes in tests and other +/// test-only functions. Mind the visibility - for `#[test_only]` it is +/// common to use `public` visibility. +public fun secret_for_testing(): u64 { + secret() +} + +#[test] +// In the test environment we have access to the `secret_for_testing` function. +fun test_multiply_by_secret() { + let expected = secret_for_testing() * 2; + assert!(multiply_by_secret(2) == expected); } ``` diff --git a/book_zh/src/move-basics/visibility.md b/book_zh/src/move-basics/visibility.md index 1e447501..5155a936 100644 --- a/book_zh/src/move-basics/visibility.md +++ b/book_zh/src/move-basics/visibility.md @@ -7,14 +7,14 @@ 在没有可见性修饰符的情况下定义在模块中的函数或结构体是私有的,它们无法从其他模块中调用。 ```move -module book::internal_visibility { - // 这个函数只能在同一模块的其他函数中调用 - fun internal() { /* ... */ } +module book::internal_visibility; - // 同一模块 -> 可以调用 internal() - fun call_internal() { - internal(); - } +// This function can be called from other functions in the same module +fun internal() { /* ... */ } + +// Same module -> can call internal() +fun call_internal() { + internal(); } ``` @@ -23,13 +23,13 @@ module book::internal_visibility { ```move -module book::try_calling_internal { - use book::internal_visibility; +module book::try_calling_internal; - // 不同模块 -> 无法调用 internal() - fun try_calling_internal() { - internal_visibility::internal(); - } +use book::internal_visibility; + +// Different module -> can't call internal() +fun try_calling_internal() { + internal_visibility::internal(); } ``` @@ -38,10 +38,10 @@ module book::try_calling_internal { 通过在 `fun` 或 `struct` 关键字前添加 `public` 关键字,可以使结构体或函数变为公共可见性。 ```move -module book::public_visibility { - // 这个函数可以从其他模块中调用 - public fun public() { /* ... */ } -} +module book::public_visibility; + +// This function can be called from other modules +public fun public() { /* ... */ } ``` 公共函数可以被导入并从其他模块中调用。以下代码将会编译通过: @@ -50,7 +50,7 @@ module book::public_visibility { module book::try_calling_public { use book::public_visibility; - // 不同模块 -> 可以调用 public() + // Different module -> can call public() fun try_calling_public() { public_visibility::public(); } @@ -62,20 +62,20 @@ module book::try_calling_public { Move 2024 引入了 _包可见性_ 修饰符。具有 _包可见性_ 的函数可以从同一包内的任何模块中调用,但不能从其他包中调用。 ```move -module book::package_visibility { - public(package) fun package_only() { /* ... */ } -} +module book::package_visibility; + +public(package) fun package_only() { /* ... */ } ``` 包函数可以从同一包内的任何模块中调用: ```move -module book::try_calling_package { - use book::package_visibility; +module book::try_calling_package; - // 同一包 `book` -> 可以调用 package_only() - fun try_calling_package() { - package_visibility::package_only(); - } +use book::package_visibility; + +// Same package `book` -> can call package_only() +fun try_calling_package() { + package_visibility::package_only(); } ``` \ No newline at end of file diff --git a/book_zh/src/programmability/capability.md b/book_zh/src/programmability/capability.md index c824f3b0..713fff3d 100644 --- a/book_zh/src/programmability/capability.md +++ b/book_zh/src/programmability/capability.md @@ -17,7 +17,7 @@ 一种常见的做法是在包发布时创建一个单独的`AdminCap`对象。这样,应用程序可以有一个设置阶段,管理员账户可以准备应用程序的状态。 ```move -{{#include ../../../packages/samples/sources/programmability/capability.move:admin_cap}} +{{#include ../../../packages/samples/sources/programmability/capability-2.move:admin_cap}} ``` ## 地址检查与能力 @@ -27,13 +27,13 @@ 让我们看一下如果使用地址检查的方式来实现创建用户的`new`函数会是什么样子: ```move -{{#include ../../../packages/samples/sources/programmability/capability.move:with_address}} +{{#include ../../../packages/samples/sources/programmability/capability-3.move:with_address}} ``` 现在,让我们看看如果使用能力的方式来实现相同的函数会是什么样子: ```move -{{#include ../../../packages/samples/sources/programmability/capability.move:with_capability}} +{{#include ../../../packages/samples/sources/programmability/capability-4.move:with_capability}} ``` 与地址检查相比,使用能力具有以下几个优势: diff --git a/book_zh/src/programmability/collections.md b/book_zh/src/programmability/collections.md index 62eb279d..0b6c23a4 100644 --- a/book_zh/src/programmability/collections.md +++ b/book_zh/src/programmability/collections.md @@ -18,6 +18,10 @@ `VecSet` 是一种用于存储一组唯一项目的集合类型。它类似于 `vector`,但它不允许重复的项目。这使得它非常 适合存储一组唯一的项目,例如唯一 ID 或地址列表。 +```move +{{#include ../../../packages/samples/sources/programmability/collections-2.move:vec_set}} +``` + VecSet 在尝试插入已存在于集合中的项时将失败。 ## VecMap @@ -29,7 +33,7 @@ VecSet 在尝试插入已存在于集合中的项时将失败。 ,则旧值将被新值替换。 ```move -{{#include ../../../packages/samples/sources/programmability/collections.move:vec_map}} +{{#include ../../../packages/samples/sources/programmability/collections-3.move:vec_map}} ``` ## 限制 @@ -44,7 +48,7 @@ VecSet 在尝试插入已存在于集合中的项时将失败。 > 此行为会由 linter 捕获并发出警告:_比较类型为 'sui::vec_set::VecSet' 的集合可能会产生意外的结果_ ```move -{{#include ../../../packages/samples/sources/programmability/collections.move:vec_set_comparison}} +{{#include ../../../packages/samples/sources/programmability/collections-4.move:vec_set_comparison}} ``` 在上面的例子中,比较会失败,因为插入顺序不是确定的,并且两个 `VecSet` 实例可能具有不同的元素顺序。即 diff --git a/book_zh/src/programmability/events.md b/book_zh/src/programmability/events.md index 8c47b375..61cdeeab 100644 --- a/book_zh/src/programmability/events.md +++ b/book_zh/src/programmability/events.md @@ -6,14 +6,16 @@ ```move // 文件:sui-framework/sources/event.move -module sui::event { - /// 发布自定义的Move事件,将数据发送到链下。 - /// - /// 用于创建自定义索引并以最适合特定应用程序的方式跟踪链上活动。 - /// - /// 类型 `T` 是索引事件的主要方式,可以包含幻象参数,例如 `emit(MyEvent)`。 - public native fun emit(event: T); -} + +module sui::event; + +/// 发出一个自定义的 Move 事件,将数据发送到链下。 +/// +/// 用于以最适合特定应用程序的方式创建自定义索引和跟踪链上活动。 +/// +/// 类型 `T` 是索引事件的主要方式,并且可以包含虚类型参数,例如 `emit(MyEvent)`。 +public native fun emit(event: T); + ``` ## 发布事件 diff --git a/book_zh/src/programmability/hot-potato-pattern.md b/book_zh/src/programmability/hot-potato-pattern.md index bd3da1d7..e19a50b7 100644 --- a/book_zh/src/programmability/hot-potato-pattern.md +++ b/book_zh/src/programmability/hot-potato-pattern.md @@ -1,4 +1,4 @@ -# 设计模式:烫手山芋 +# 设计模式:烫手山芋模式 在能力系统中,没有任何能力的结构体被称为 _烫手山芋_ 。 它不能被存储(既不能作为[对象](./../storage/key-ability.md), @@ -94,9 +94,9 @@ transfer::public_transfer(proceeds, ctx.sender()); 该模式在 Sui Framework中以各种形式使用。以下是一些示例: -- `sui::borrow` - 使用“烫手山芋”模式确保借出的值被正确归还到原始容器。 -- `sui::transfer_policy` - 定义了 `TransferRequest`,一种只能在满足所有条件时才能被消耗的“烫手山芋”。 -- `sui::token` - 在闭环代币系统中,`ActionRequest` 携带已执行操作的信息,并像 `TransferRequest` 一样收集批准。 +- [sui::borrow](https://docs.sui.io/references/framework/sui-framework/borrow) - 使用“烫手山芋”模式,确保借用的值被返回到正确的容器。 +- [sui::transfer_policy](https://docs.sui.io/references/framework/sui-framework/transfer_policy) - 定义了 `TransferRequest`,一种“烫手山芋”对象,只有在满足所有条件时才能被消费。 +- [sui::token](https://docs.sui.io/references/framework/sui-framework/token) - 在闭环代币系统中,`ActionRequest` 用于携带已执行操作的信息,并类似于 `TransferRequest`,收集批准。 ## 总结 diff --git a/book_zh/src/programmability/module-initializer.md b/book_zh/src/programmability/module-initializer.md index b853135c..2bf9d393 100644 --- a/book_zh/src/programmability/module-initializer.md +++ b/book_zh/src/programmability/module-initializer.md @@ -11,7 +11,7 @@ 在同一个包中,另一个模块可以有自己的`init`函数,封装不同的逻辑。 ```move -{{#include ../../../packages/samples/sources/programmability/module-initializer.move:other}} +{{#include ../../../packages/samples/sources/programmability/module-initializer-2.move:other}} ``` ## `init`功能 diff --git a/book_zh/src/programmability/sui-framework.md b/book_zh/src/programmability/sui-framework.md index 6bc7b4fd..405d4c3d 100644 --- a/book_zh/src/programmability/sui-framework.md +++ b/book_zh/src/programmability/sui-framework.md @@ -43,9 +43,12 @@ Sui Framework 是 [Package Manifest](./../concepts/manifest.md) 中的默认依
-| 模块 | 描述 | 章节 | -| -------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------ | -| [sui::bcs](https://docs.sui.io/references/framework/sui-framework/bcs) | 实现了 BCS 编码和解码函数 | [二进制规范序列化](./bcs.md) | +| 模块 | 描述 | 章节 | +| ----------------------------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------- | +| [sui::bcs](https://docs.sui.io/references/framework/sui-framework/bcs) | 实现了 BCS(Binary Canonical Serialization)编码和解码函数 | [二进制规范序列化](./bcs.md) | +| [sui::borrow](https://docs.sui.io/references/framework/sui-framework/borrow) | 实现了通过**值**借用的借用机制 | [烫手山芋模式](./hot-potato-pattern.md) | +| [sui::hex](https://docs.sui.io/references/framework/sui-framework/hex) | 实现了十六进制编码和解码函数 | - | +| [sui::types](https://docs.sui.io/references/framework/sui-framework/types) | 提供了一种检查类型是否为一次性见证(One-Time-Witness)的方法 | [一次性见证](./one-time-witness.md) | ## 导出地址 @@ -93,11 +96,12 @@ Sui Framework 的源代码可以在 [Sui 仓库](https://github.com/MystenLabs/s - sui::transfer_policy -- sui::bcs -- sui::hex -- sui::math -- sui::types -- sui::borrow +工具: ++ sui::bcs ++ sui::hex +- sui::math (deprecated) ++ sui::types ++ sui::borrow - sui::authenticator diff --git a/book_zh/src/programmability/witness-pattern.md b/book_zh/src/programmability/witness-pattern.md index 826a25ed..326a58c9 100644 --- a/book_zh/src/programmability/witness-pattern.md +++ b/book_zh/src/programmability/witness-pattern.md @@ -11,30 +11,30 @@ > 通常情况下,证明者结构体不会被存储,因此函数可能需要该类型的 [Drop](./../move-basics/drop-ability.md) 能力。 ```move -module book::witness { - /// 需要证明者才能创建的结构体。 - public struct Instance { t: T } - - /// 使用提供的 T 创建 `Instance` 的新实例。 - public fun new(witness: T): Instance { - Instance { t: witness } - } +module book::witness; + +/// A struct that requires a witness to be created. +public struct Instance { t: T } + +/// Create a new instance of `Instance` with the provided T. +public fun new(witness: T): Instance { + Instance { t: witness } } ``` 构造 `Instance` 的唯一方法是调用 `new` 函数,并提供类型 `T` 的一个实例。这是 Move 中证明者模式的基本示例。提供证明者的模块通常会有相应的实现,例如下面的 `book::witness_source` 模块: ```move -module book::witness_source { - use book::witness::{Self, Instance}; +module book::witness_source; + +use book::witness::{Self, Instance}; - /// 作为证明者使用的结构体。 - public struct W {} +/// A struct used as a witness. +public struct W {} - /// 创建 `Instance` 的新实例。 - public fun new_instance(): Instance { - witness::new(W {}) - } +/// Create a new instance of `Instance`. +public fun new_instance(): Instance { + witness::new(W {}) } ``` diff --git a/book_zh/src/storage/key-ability.md b/book_zh/src/storage/key-ability.md index 4f334800..4ff3d831 100644 --- a/book_zh/src/storage/key-ability.md +++ b/book_zh/src/storage/key-ability.md @@ -37,5 +37,5 @@ public fun new(name: String, ctx: &mut TxContext): Object { ## 进一步阅读 -- 在 Move 参考手册中的[类型能力](/reference/type-abilities.html)。 +- 在 Move 参考手册中的[类型能力](/reference/abilities.html)。 diff --git a/book_zh/src/storage/store-ability.md b/book_zh/src/storage/store-ability.md index eaf3ffdb..51d89268 100644 --- a/book_zh/src/storage/store-ability.md +++ b/book_zh/src/storage/store-ability.md @@ -46,4 +46,4 @@ public struct MegaConfig has key { ## 进一步阅读 -- Move参考中的[类型能力](/reference/type-abilities.html)。 \ No newline at end of file +- Move参考中的[类型能力](/reference/abilities.html)。 \ No newline at end of file diff --git a/book_zh/src/your-first-move/hello-world.md b/book_zh/src/your-first-move/hello-world.md index 3a9b92d9..177a57b2 100644 --- a/book_zh/src/your-first-move/hello-world.md +++ b/book_zh/src/your-first-move/hello-world.md @@ -68,9 +68,7 @@ hello_world = "0x0" ```move /* /// 模块:hello_world -module hello_world::hello_world { - -} +module hello_world::hello_world; */ ``` @@ -92,21 +90,20 @@ module hello_world::hello_world { ```move /* #[test_only] -module hello_world::hello_world_tests { - // uncomment this line to import the module - // use hello_world::hello_world; +module hello_world::hello_world_tests; +// uncomment this line to import the module +// use hello_world::hello_world; - const ENotImplemented: u64 = 0; +const ENotImplemented: u64 = 0; - #[test] - fun test_hello_world() { - // pass - } +#[test] +fun test_hello_world() { + // pass +} - #[test, expected_failure(abort_code = hello_world::hello_world_tests::ENotImplemented)] - fun test_hello_world_fail() { - abort ENotImplemented - } +#[test, expected_failure(abort_code = hello_world::hello_world_tests::ENotImplemented)] +fun test_hello_world_fail() { + abort ENotImplemented } */ ``` @@ -125,13 +122,15 @@ Move 是一种编译型语言,因此它需要将源文件编译成 Move 字节 ```move /// 命名地址 `hello_world` 下的 `hello_world` 模块。 /// 命名地址在 `Move.toml` 中设置。 -module hello_world::hello_world { - // 从标准库导入 `String` 类型 - use std::string::String; - /// 返回 "Hello, World!" 作为 `String`。 - public fun hello_world(): String { - b"Hello, World!".to_string() - } + +module hello_world::hello_world; +// Imports the `String` type from the Standard Library + +use std::string::String; + +/// Returns the "Hello, World!" as a `String`. +public fun hello_world(): String { + b"Hello, World!".to_string() } ``` @@ -164,13 +163,13 @@ BUILDING hello_world 请用以下内容替换 `tests/hello_world_tests.move`: ```move -#[test_only] -module hello_world::hello_world_tests { - use hello_world::hello_world; - #[test] - fun test_hello_world() { - assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0); - } +module hello_world::hello_world_tests; + +use hello_world::hello_world; + +#[test] +fun test_hello_world() { + assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0); } ``` diff --git a/packages/hello_world/sources/hello_world.move b/packages/hello_world/sources/hello_world.move index a3e3fb05..2bbbee7d 100644 --- a/packages/hello_world/sources/hello_world.move +++ b/packages/hello_world/sources/hello_world.move @@ -1,11 +1,11 @@ /// The module `hello_world` under named address `hello_world`. /// The named address is set in the `Move.toml`. -module hello_world::hello_world { - // Imports the `String` type from the Standard Library - use std::string::String; +module hello_world::hello_world; - /// Returns the "Hello World!" as a `String`. - public fun hello_world(): String { - b"Hello, World!".to_string() - } +// Imports the `String` type from the Standard Library +use std::string::String; + +/// Returns the "Hello World!" as a `String`. +public fun hello_world(): String { + b"Hello, World!".to_string() } diff --git a/packages/hello_world/tests/hello_world_tests.move b/packages/hello_world/tests/hello_world_tests.move index 4285814a..582c5b06 100644 --- a/packages/hello_world/tests/hello_world_tests.move +++ b/packages/hello_world/tests/hello_world_tests.move @@ -1,9 +1,9 @@ #[test_only] -module hello_world::hello_world_tests { - use hello_world::hello_world; +module hello_world::hello_world_tests; - #[test] - fun test_hello_world() { - assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0); - } +use hello_world::hello_world; + +#[test] +fun test_hello_world() { + assert!(hello_world::hello_world() == b"Hello, World!".to_string(), 0); } diff --git a/packages/samples/sources/move-basics/address.move b/packages/samples/sources/move-basics/address.move index 16212e12..4a65781d 100644 --- a/packages/samples/sources/move-basics/address.move +++ b/packages/samples/sources/move-basics/address.move @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #[allow(unused_variable)] -module book::address { +module book::address; #[test] fun address_literal() { @@ -24,7 +24,6 @@ use sui::address; let addr_as_u256: u256 = address::to_u256(@0x1); let addr = address::from_u256(addr_as_u256); // ANCHOR_END: to_u256 - } #[test] fun address_string() { @@ -35,7 +34,6 @@ use std::string::String; let addr_as_string: String = address::to_string(@0x1); // ANCHOR_END: to_string - } #[test] fun address_bytes() { @@ -46,6 +44,4 @@ use sui::address; let addr_as_u8: vector = address::to_bytes(@0x1); let addr = address::from_bytes(addr_as_u8); // ANCHOR_END: to_bytes - -} } diff --git a/packages/samples/sources/move-basics/assert-and-abort.move b/packages/samples/sources/move-basics/assert-and-abort.move index a388bb7d..528a81fb 100644 --- a/packages/samples/sources/move-basics/assert-and-abort.move +++ b/packages/samples/sources/move-basics/assert-and-abort.move @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -module book::assert_abort { +module book::assert_abort; #[test, expected_failure(abort_code = 1, location=Self)] fun test_abort() { @@ -50,4 +50,22 @@ public fun update_record(/* ... , */ user_has_access: bool, field_exists: bool) /* ... */ } // ANCHOR_END: error_const + +public struct User { is_authorized: bool, value: u64 } + +// ANCHOR: error_attribute +#[error] +const ENotAuthorized: vector = b"The user is not authorized to perform this action"; + +#[error] +const EValueTooLow: vector = b"The value is too low, it should be at least 10"; + +/// Performs an action on behalf of the user. +public fun update_value(user: &mut User, value: u64) { + assert!(user.is_authorized, ENotAuthorized); + assert!(value >= 10, EValueTooLow); + + user.value = value; +} +// ANCHOR_END: error_attribute } diff --git a/packages/samples/sources/move-basics/comments-block.move b/packages/samples/sources/move-basics/comments-block.move new file mode 100644 index 00000000..b5a658f8 --- /dev/null +++ b/packages/samples/sources/move-basics/comments-block.move @@ -0,0 +1,20 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_function)] +// ANCHOR: main +module book::comments_block; + +fun /* you can comment everywhere */ go_wild() { + /* here + there + everywhere */ let a = 10; + let b = /* even here */ 10; /* and again */ + a + b; +} +/* you can use it to remove certain expressions or definitions +fun empty_commented_out() { + +} +*/ +// ANCHOR_END: main diff --git a/packages/samples/sources/move-basics/comments-doc.move b/packages/samples/sources/move-basics/comments-doc.move new file mode 100644 index 00000000..b97d08bd --- /dev/null +++ b/packages/samples/sources/move-basics/comments-doc.move @@ -0,0 +1,21 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_function, unused_const, unused_variable, unused_field)] +// ANCHOR: main +/// Module has documentation! +module book::comments_doc; + +/// This is a 0x0 address constant! +const AN_ADDRESS: address = @0x0; + +/// This is a struct! +public struct AStruct { + /// This is a field of a struct! + a_field: u8, +} + +/// This function does something! +/// And it's documented! +fun do_something() {} +// ANCHOR_END: main diff --git a/packages/samples/sources/move-basics/comments-line.move b/packages/samples/sources/move-basics/comments-line.move new file mode 100644 index 00000000..8a2a4d9b --- /dev/null +++ b/packages/samples/sources/move-basics/comments-line.move @@ -0,0 +1,15 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_function, unused_variable)] +// ANCHOR: main +module book::comments_line; + +// let's add a note to everything! +fun some_function_with_numbers() { + let a = 10; + // let b = 10 this line is commented and won't be executed + let b = 5; // here comment is placed after code + a + b; // result is 15, not 10! +} +// ANCHOR_END: main diff --git a/packages/samples/sources/move-basics/comments.move b/packages/samples/sources/move-basics/comments.move deleted file mode 100644 index 8675fa4b..00000000 --- a/packages/samples/sources/move-basics/comments.move +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#[allow(unused_function)] -// ANCHOR: block -module book::comments_block { - fun /* you can comment everywhere */ go_wild() { - /* here - there - everywhere */ let a = 10; - let b = /* even here */ 10; /* and again */ - a + b; - } - /* you can use it to remove certain expressions or definitions - fun empty_commented_out() { - - } - */ -} -// ANCHOR_END: block - -#[allow(unused_function, unused_const, unused_variable, unused_field)] -// ANCHOR: doc -/// Module has documentation! -module book::comments_doc { - - /// This is a 0x0 address constant! - const AN_ADDRESS: address = @0x0; - - /// This is a struct! - public struct AStruct { - /// This is a field of a struct! - a_field: u8, - } - - /// This function does something! - /// And it's documented! - fun do_something() {} -} -// ANCHOR_END: doc - -#[allow(unused_function, unused_variable)] -// ANCHOR: line_2 -module book::comments_line_2 { - // let's add a note to everything! - fun some_function_with_numbers() { - let a = 10; - // let b = 10 this line is commented and won't be executed - let b = 5; // here comment is placed after code - a + b; // result is 15, not 10! - } -} -// ANCHOR_END: line_2 - -#[allow(unused_function)] -// ANCHOR: line -module book::comments_line { - fun some_function() { - // this is a comment line - } -} -// ANCHOR_END: line diff --git a/packages/samples/sources/move-basics/constants-config.move b/packages/samples/sources/move-basics/constants-config.move new file mode 100644 index 00000000..f6bfbab9 --- /dev/null +++ b/packages/samples/sources/move-basics/constants-config.move @@ -0,0 +1,17 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: config +module book::config; + +const ITEM_PRICE: u64 = 100; +const TAX_RATE: u64 = 10; +const SHIPPING_COST: u64 = 5; + +/// Returns the price of an item. +public fun item_price(): u64 { ITEM_PRICE } +/// Returns the tax rate. +public fun tax_rate(): u64 { TAX_RATE } +/// Returns the shipping cost. +public fun shipping_cost(): u64 { SHIPPING_COST } +// ANCHOR_END: config diff --git a/packages/samples/sources/move-basics/constants-naming.move b/packages/samples/sources/move-basics/constants-naming.move new file mode 100644 index 00000000..38a94c9e --- /dev/null +++ b/packages/samples/sources/move-basics/constants-naming.move @@ -0,0 +1,13 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_const)] +module book::naming; + +// ANCHOR: naming +/// Price of the item used at the shop. +const ITEM_PRICE: u64 = 100; + +/// Error constant. +const EItemNotFound: u64 = 1; +// ANCHOR_END: naming diff --git a/packages/samples/sources/move-basics/constants-shop-price.move b/packages/samples/sources/move-basics/constants-shop-price.move new file mode 100644 index 00000000..165a5ff8 --- /dev/null +++ b/packages/samples/sources/move-basics/constants-shop-price.move @@ -0,0 +1,25 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: shop_price +module book::shop_price; + +use sui::{coin::Coin, sui::SUI}; + +/// The price of an item in the shop. +const ITEM_PRICE: u64 = 100; +/// The owner of the shop, an address. +const SHOP_OWNER: address = @0xa11ce; + +/// An item sold in the shop. +public struct Item {} + +/// Purchase an item from the shop. +public fun purchase(coin: Coin): Item { + assert!(coin.value() == ITEM_PRICE); + + transfer::public_transfer(coin, SHOP_OWNER); + + Item {} +} +// ANCHOR_END: shop_price diff --git a/packages/samples/sources/move-basics/constants.move b/packages/samples/sources/move-basics/constants.move index 3556f72b..36d5300e 100644 --- a/packages/samples/sources/move-basics/constants.move +++ b/packages/samples/sources/move-basics/constants.move @@ -1,67 +1,16 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -module book::constants { - const MAX: u64 = 100; +module book::constants; - // however you can pass constant outside using a function - public fun max(): u64 { - MAX - } +const MAX: u64 = 100; - // or using - public fun is_max(num: u64): bool { - num == MAX - } +// however you can pass constant outside using a function +public fun max(): u64 { + MAX } -// ANCHOR: shop_price -module book::shop_price { - use sui::coin::Coin; - use sui::sui::SUI; - - /// The price of an item in the shop. - const ITEM_PRICE: u64 = 100; - /// The owner of the shop, an address. - const SHOP_OWNER: address = @0xa11ce; - - /// An item sold in the shop. - public struct Item { /* ... */ } - - /// Purchase an item from the shop. - public fun purchase(coin: Coin): Item { - assert!(coin.value() == ITEM_PRICE, 0); - - transfer::public_transfer(coin, SHOP_OWNER); - - Item { /* ... */ } - } -} -// ANCHOR_END: shop_price -#[allow(unused_const)] -module book::naming { - -// ANCHOR: naming -/// Price of the item used at the shop. -const ITEM_PRICE: u64 = 100; - -/// Error constant. -const EItemNotFound: u64 = 1; -// ANCHOR_END: naming - -} - -// ANCHOR: config -module book::config { - const ITEM_PRICE: u64 = 100; - const TAX_RATE: u64 = 10; - const SHIPPING_COST: u64 = 5; - - /// Returns the price of an item. - public fun item_price(): u64 { ITEM_PRICE } - /// Returns the tax rate. - public fun tax_rate(): u64 { TAX_RATE } - /// Returns the shipping cost. - public fun shipping_cost(): u64 { SHIPPING_COST } +// or using +public fun is_max(num: u64): bool { + num == MAX } -// ANCHOR_END: config diff --git a/packages/samples/sources/move-basics/control-flow.move b/packages/samples/sources/move-basics/control-flow.move index a583a0ee..27c71a8b 100644 --- a/packages/samples/sources/move-basics/control-flow.move +++ b/packages/samples/sources/move-basics/control-flow.move @@ -2,152 +2,150 @@ // SPDX-License-Identifier: Apache-2.0 // ANCHOR: module -module book::control_flow { +module book::control_flow; // ANCHOR_END: module - // ANCHOR: if_condition - #[test] - fun test_if() { - let x = 5; +// ANCHOR: if_condition +#[test] +fun test_if() { + let x = 5; - // `x > 0` is a boolean expression. - if (x > 0) { - std::debug::print(&b"X is bigger than 0".to_string()) - }; - } - // ANCHOR_END: if_condition - // ANCHOR: if_else - #[test] - fun test_if_else() { - let x = 5; - let y = if (x > 0) { - 1 - } else { - 0 - }; - - assert!(y == 1, 0); - } - // ANCHOR_END: if_else - // ANCHOR: while_loop - // This function iterates over the `x` variable until it reaches 10, the - // return value is the number of iterations it took to reach 10. - // - // If `x` is 0, then the function will return 10. - // If `x` is 5, then the function will return 5. - fun while_loop(mut x: u8): u8 { - let mut y = 0; - - // This will loop until `x` is 10. - // And will never run if `x` is 10 or more. - while (x < 10) { - y = y + 1; - x = x + 1; - }; - - y - } - - #[test] - fun test_while() { - assert!(while_loop(0) == 10, 0); // 10 times - assert!(while_loop(5) == 5, 0); // 5 times - assert!(while_loop(10) == 0, 0); // loop never executed - } - // ANCHOR_END: while_loop - // ANCHOR: infinite_while - #[test, expected_failure(out_of_gas, location=Self)] - fun test_infinite_while() { - let mut x = 0; - - // This will loop forever. - while (true) { - x = x + 1; - }; - - // This line will never be executed. - assert!(x == 5, 0); - } - // ANCHOR_END: infinite_while - #[allow(dead_code)] - // ANCHOR: infinite_loop - #[test, expected_failure(out_of_gas, location=Self)] - fun test_infinite_loop() { - let mut x = 0; - - // This will loop forever. - loop { - x = x + 1; - }; + // `x > 0` is a boolean expression. + if (x > 0) { + std::debug::print(&b"X is bigger than 0".to_string()) + }; +} +// ANCHOR_END: if_condition +// ANCHOR: if_else +#[test] +fun test_if_else() { + let x = 5; + let y = if (x > 0) { + 1 + } else { + 0 + }; + + assert!(y == 1); +} +// ANCHOR_END: if_else +// ANCHOR: while_loop +// This function iterates over the `x` variable until it reaches 10, the +// return value is the number of iterations it took to reach 10. +// +// If `x` is 0, then the function will return 10. +// If `x` is 5, then the function will return 5. +fun while_loop(mut x: u8): u8 { + let mut y = 0; + + // This will loop until `x` is 10. + // And will never run if `x` is 10 or more. + while (x < 10) { + y = y + 1; + x = x + 1; + }; + + y +} - // This line will never be executed. - assert!(x == 5, 0); - } - // ANCHOR_END: infinite_loop - // ANCHOR: break_loop - #[test] - fun test_break_loop() { - let mut x = 0; - - // This will loop until `x` is 5. - loop { - x = x + 1; - - // If `x` is 5, then exit the loop. - if (x == 5) { - break // Exit the loop. - } - }; +#[test] +fun test_while() { + assert!(while_loop(0) == 10); // 10 times + assert!(while_loop(5) == 5); // 5 times + assert!(while_loop(10) == 0); // loop never executed +} +// ANCHOR_END: while_loop +// ANCHOR: infinite_while +#[test, expected_failure(out_of_gas, location=Self)] +fun test_infinite_while() { + let mut x = 0; + + // This will loop forever. + while (true) { + x = x + 1; + }; + + // This line will never be executed. + assert!(x == 5); +} +// ANCHOR_END: infinite_while +#[allow(dead_code)] +// ANCHOR: infinite_loop +#[test, expected_failure(out_of_gas, location=Self)] +fun test_infinite_loop() { + let mut x = 0; + + // This will loop forever. + loop { + x = x + 1; + }; + + // This line will never be executed. + assert!(x == 5); +} +// ANCHOR_END: infinite_loop +// ANCHOR: break_loop +#[test] +fun test_break_loop() { + let mut x = 0; - assert!(x == 5, 0); - } - // ANCHOR_END: break_loop - // ANCHOR: continue_loop - #[test] - fun test_continue_loop() { - let mut x = 0; - - // This will loop until `x` is 10. - loop { - x = x + 1; - - // If `x` is odd, then skip the rest of the iteration. - if (x % 2 == 1) { - continue // Skip the rest of the iteration. - }; - - std::debug::print(&x); - - // If `x` is 10, then exit the loop. - if (x == 10) { - break // Exit the loop. - } - }; + // This will loop until `x` is 5. + loop { + x = x + 1; - assert!(x == 10, 0); // 10 - } - // ANCHOR_END: continue_loop - // ANCHOR: return_statement - /// This function returns `true` if `x` is greater than 0 and not 5, - /// otherwise it returns `false`. - fun is_positive(x: u8): bool { + // If `x` is 5, then exit the loop. if (x == 5) { - return false - }; + break // Exit the loop. + } + }; - if (x > 0) { - return true + assert!(x == 5); +} +// ANCHOR_END: break_loop +// ANCHOR: continue_loop +#[test] +fun test_continue_loop() { + let mut x = 0; + + // This will loop until `x` is 10. + loop { + x = x + 1; + + // If `x` is odd, then skip the rest of the iteration. + if (x % 2 == 1) { + continue // Skip the rest of the iteration. }; - false - } + std::debug::print(&x); + + // If `x` is 10, then exit the loop. + if (x == 10) { + break // Exit the loop. + } + }; - #[test] - fun test_return() { - assert!(is_positive(5) == false, 0); - assert!(is_positive(0) == false, 0); - assert!(is_positive(1) == true, 0); - } - // ANCHOR_END: return_statement + assert!(x == 10); // 10 +} +// ANCHOR_END: continue_loop +// ANCHOR: return_statement +/// This function returns `true` if `x` is greater than 0 and not 5, +/// otherwise it returns `false`. +fun is_positive(x: u8): bool { + if (x == 5) { + return false + }; + + if (x > 0) { + return true + }; + + false +} +#[test] +fun test_return() { + assert!(is_positive(5) == false); + assert!(is_positive(0) == false); + assert!(is_positive(1) == true); } +// ANCHOR_END: return_statement diff --git a/packages/samples/sources/move-basics/copy-ability.move b/packages/samples/sources/move-basics/copy-ability.move index c76c748b..148532d1 100644 --- a/packages/samples/sources/move-basics/copy-ability.move +++ b/packages/samples/sources/move-basics/copy-ability.move @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #[allow(unused_variable)] -module book::copy_ability { +module book::copy_ability; // ANCHOR: copyable public struct Copyable has copy {} @@ -24,4 +24,3 @@ let Copyable {} = c; // doesn't have `drop` // ANCHOR: copy_drop public struct Value has copy, drop {} // ANCHOR_END: copy_drop -} diff --git a/packages/samples/sources/move-basics/drop-ability.move b/packages/samples/sources/move-basics/drop-ability.move index 5e313dfc..1397e8b9 100644 --- a/packages/samples/sources/move-basics/drop-ability.move +++ b/packages/samples/sources/move-basics/drop-ability.move @@ -2,26 +2,25 @@ // SPDX-License-Identifier: Apache-2.0 // ANCHOR: main -module book::drop_ability { +module book::drop_ability; - /// This struct has the `drop` ability. - public struct IgnoreMe has drop { - a: u8, - b: u8, - } +/// This struct has the `drop` ability. +public struct IgnoreMe has drop { + a: u8, + b: u8, +} - /// This struct does not have the `drop` ability. - public struct NoDrop {} +/// This struct does not have the `drop` ability. +public struct NoDrop {} - #[test] - // Create an instance of the `IgnoreMe` struct and ignore it. - // Even though we constructed the instance, we don't need to unpack it. - fun test_ignore() { - let no_drop = NoDrop {}; - let _ = IgnoreMe { a: 1, b: 2 }; // no need to unpack +#[test] +// Create an instance of the `IgnoreMe` struct and ignore it. +// Even though we constructed the instance, we don't need to unpack it. +fun test_ignore() { + let no_drop = NoDrop {}; + let _ = IgnoreMe { a: 1, b: 2 }; // no need to unpack - // The value must be unpacked for the code to compile. - let NoDrop {} = no_drop; // OK - } + // The value must be unpacked for the code to compile. + let NoDrop {} = no_drop; // OK } // ANCHOR_END: main diff --git a/packages/samples/sources/move-basics/expression.move b/packages/samples/sources/move-basics/expression.move index 2f3b7d13..a770397d 100644 --- a/packages/samples/sources/move-basics/expression.move +++ b/packages/samples/sources/move-basics/expression.move @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #[allow(unused_variable)] -module book::expression { +module book::expression; #[test] fun expression_examples() { @@ -16,8 +16,8 @@ let a; let b = true; // true is a literal let n = 1000; // 1000 is a literal let h = 0x0A; // 0x0A is a literal -let v = b"hello"; // b'hello' is a byte vector literal -let x = x"0A"; // x'0A' is a byte vector literal +let v = b"hello"; // b"hello" is a byte vector literal +let x = x"0A"; // x"0A" is a byte vector literal let c = vector[1, 2, 3]; // vector[] is a vector literal // ANCHOR_END: literals @@ -81,7 +81,4 @@ while (bool_expr) { expr; }; // loop is an expression, but returns `()` as well. loop { expr; break }; // ANCHOR_END: control_flow - - -} } diff --git a/packages/samples/sources/move-basics/function.move b/packages/samples/sources/move-basics/function.move index 3a2c794c..e93aaaf1 100644 --- a/packages/samples/sources/move-basics/function.move +++ b/packages/samples/sources/move-basics/function.move @@ -1,46 +1,29 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -#[allow(unused_variable)] +#[allow(unused_variable, unused_function, unused_let_mut)] // ANCHOR: math -module book::math { - /// Function takes two arguments of type `u64` and returns their sum. - /// The `public` visibility modifier makes the function accessible from - /// outside the module. - public fun add(a: u64, b: u64): u64 { - a + b - } +module book::math; - #[test] - fun test_add() { - let sum = add(1, 2); - assert!(sum == 3, 0); - } +/// Function takes two arguments of type `u64` and returns their sum. +/// The `public` visibility modifier makes the function accessible from +/// outside the module. +public fun add(a: u64, b: u64): u64 { + a + b +} + +#[test] +fun test_add() { + let sum = add(1, 2); + assert!(sum == 3); } // ANCHOR_END: math -#[allow(unused_variable, unused_function)] -module book::function_declaration { + // ANCHOR: return_nothing fun return_nothing() { // empty expression, function returns `()` } // ANCHOR_END: return_nothing -} - -#[allow(unused_variable, unused_function)] -// ANCHOR: use_math -module book::use_math { - use book::math; - - fun call_add() { - // function is called via the path - let sum = math::add(1, 2); - } -} -// ANCHOR_END: use_math - -#[allow(unused_variable, unused_let_mut)] -module book::function_multi_return { // ANCHOR: tuple_return fun get_name_and_age(): (vector, u8) { @@ -53,8 +36,8 @@ fun get_name_and_age(): (vector, u8) { // Tuple must be destructured to access its elements. // Name and age are declared as immutable variables. let (name, age) = get_name_and_age(); -assert!(name == b"John", 0); -assert!(age == 25, 0); +assert!(name == b"John"); +assert!(age == 25); // ANCHOR_END: tuple_return_imm // ANCHOR: tuple_return_mut @@ -67,4 +50,3 @@ let (mut name, age) = get_name_and_age(); let (_, age) = get_name_and_age(); // ANCHOR_END: tuple_return_ignore } -} diff --git a/packages/samples/sources/move-basics/function_use.move b/packages/samples/sources/move-basics/function_use.move new file mode 100644 index 00000000..ff528bcd --- /dev/null +++ b/packages/samples/sources/move-basics/function_use.move @@ -0,0 +1,14 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_variable, unused_function)] +// ANCHOR: use_math +module book::use_math; + +use book::math; + +fun call_add() { + // function is called via the path + let sum = math::add(1, 2); +} +// ANCHOR_END: use_math diff --git a/packages/samples/sources/move-basics/generics.move b/packages/samples/sources/move-basics/generics.move index b1f778d4..7ef6bfaf 100644 --- a/packages/samples/sources/move-basics/generics.move +++ b/packages/samples/sources/move-basics/generics.move @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 #[allow(unused_variable, unused_field)] -module book::generics { +module book::generics; // ANCHOR: container /// Container for any type `T`. @@ -29,10 +29,6 @@ fun test_container() { let Container { value: _ } = container; } // ANCHOR_END: test_container -} - -#[allow(unused_variable, unused_field)] -module book::generics_multiple { // ANCHOR: pair /// A pair of values of any type `T` and `U`. @@ -78,15 +74,12 @@ fun test_swap_type_params() { let Pair { first: pf1, second: ps1 } = pair1; // first1: u8, second1: bool let Pair { first: pf2, second: ps2 } = pair2; // first2: bool, second2: u8 - assert!(pf1 == ps2, 0x0); // 10 == 10 - assert!(ps1 == pf2, 0x0); // true == true + assert!(pf1 == ps2); // 10 == 10 + assert!(ps1 == pf2); // true == true } // ANCHOR_END: test_pair_swap -} -#[allow(unused_variable, unused_field)] -module book::generics_user { - use std::string::String; +use std::string::String; // ANCHOR: user /// A user record with name, age, and some generic metadata @@ -109,10 +102,6 @@ public fun update_age(user: &mut User, age: u8) { user.age = age; } // ANCHOR_END: update_user -} - -#[allow(unused_variable, unused_field)] -module book::generics_phantom { // ANCHOR: phantom /// A generic type with a phantom type parameter. @@ -135,10 +124,6 @@ fun test_phantom_type() { let Coin { value: _ } = coin2; } // ANCHOR_END: test_phantom -} - -#[allow(unused_variable, unused_field)] -module book::generics_constraints { // ANCHOR: constraints /// A generic type with a type parameter that has the `drop` ability. @@ -165,5 +150,3 @@ fun test_constraints() { // let copyable_droppable = CopyableDroppable { value: 10 }; } // ANCHOR_END: test_constraints - -} diff --git a/packages/samples/sources/move-basics/importing-modules-conflict-resolution.move b/packages/samples/sources/move-basics/importing-modules-conflict-resolution.move new file mode 100644 index 00000000..bd04bfb0 --- /dev/null +++ b/packages/samples/sources/move-basics/importing-modules-conflict-resolution.move @@ -0,0 +1,15 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_use)] +// ANCHOR: conflict +module book::conflict_resolution; + +// `as` can be placed after any import, including group imports +use book::module_one::{Self as mod, Character as Char}; + +/// Calls the `new` function from the `module_one` module. +public fun create(): Char { + mod::new() +} +// ANCHOR_END: conflict diff --git a/packages/samples/sources/move-basics/importing-modules-external.move b/packages/samples/sources/move-basics/importing-modules-external.move new file mode 100644 index 00000000..f9800296 --- /dev/null +++ b/packages/samples/sources/move-basics/importing-modules-external.move @@ -0,0 +1,10 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_use)] +// ANCHOR: external +module book::imports; + +use std::string; // std = 0x1, string is a module in the standard library +use sui::coin; // sui = 0x2, coin is a module in the Sui Framework +// ANCHOR_END: external diff --git a/packages/samples/sources/move-basics/importing-modules-grouped.move b/packages/samples/sources/move-basics/importing-modules-grouped.move new file mode 100644 index 00000000..952c71e6 --- /dev/null +++ b/packages/samples/sources/move-basics/importing-modules-grouped.move @@ -0,0 +1,16 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_use)] +// ANCHOR: grouped +module book::grouped_imports; + +// imports the `new` function and the `Character` struct from +// the `module_one` module +use book::module_one::{new, Character}; + +/// Calls the `new` function from the `module_one` module. +public fun create_character(): Character { + new() +} +// ANCHOR_END: grouped diff --git a/packages/samples/sources/move-basics/importing-modules-members.move b/packages/samples/sources/move-basics/importing-modules-members.move new file mode 100644 index 00000000..91472676 --- /dev/null +++ b/packages/samples/sources/move-basics/importing-modules-members.move @@ -0,0 +1,15 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_use)] +// ANCHOR: members +module book::more_imports; + +use book::module_one::new; // imports the `new` function from the `module_one` module +use book::module_one::Character; // importing the `Character` struct from the `module_one` module + +/// Calls the `new` function from the `module_one` module. +public fun create_character(): Character { + new() +} +// ANCHOR_END: members diff --git a/packages/samples/sources/move-basics/importing-modules-self.move b/packages/samples/sources/move-basics/importing-modules-self.move new file mode 100644 index 00000000..02682277 --- /dev/null +++ b/packages/samples/sources/move-basics/importing-modules-self.move @@ -0,0 +1,15 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_use)] +// ANCHOR: self +module book::self_imports; + +// imports the `Character` struct, and the `module_one` module +use book::module_one::{Self, Character}; + +/// Calls the `new` function from the `module_one` module. +public fun create_character(): Character { + module_one::new() +} +// ANCHOR_END: self diff --git a/packages/samples/sources/move-basics/importing-modules-two.move b/packages/samples/sources/move-basics/importing-modules-two.move new file mode 100644 index 00000000..d62e5483 --- /dev/null +++ b/packages/samples/sources/move-basics/importing-modules-two.move @@ -0,0 +1,14 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_use)] +// ANCHOR: module_two +module book::module_two; + +use book::module_one; // importing module_one from the same package + +/// Calls the `new` function from the `module_one` module. +public fun create_and_ignore() { + let _ = module_one::new(); +} +// ANCHOR_END: module_two diff --git a/packages/samples/sources/move-basics/importing-modules.move b/packages/samples/sources/move-basics/importing-modules.move index d86f86be..bca39336 100644 --- a/packages/samples/sources/move-basics/importing-modules.move +++ b/packages/samples/sources/move-basics/importing-modules.move @@ -3,84 +3,11 @@ #[allow(unused_use)] // ANCHOR: module_one -module book::module_one { - /// Struct defined in the same module. - public struct Character has drop {} +module book::module_one; - /// Simple function that creates a new `Character` instance. - public fun new(): Character { Character {} } -} -// ANCHOR_END: module_one - -#[allow(unused_use)] -// ANCHOR: module_two -module book::module_two { - use book::module_one; // importing module_one from the same package - - /// Calls the `new` function from the `module_one` module. - public fun create_and_ignore() { - let _ = module_one::new(); - } -} -// ANCHOR_END: module_two - -#[allow(unused_use)] -// ANCHOR: members -module book::more_imports { - use book::module_one::new; // imports the `new` function from the `module_one` module - use book::module_one::Character; // importing the `Character` struct from the `module_one` module - - /// Calls the `new` function from the `module_one` module. - public fun create_character(): Character { - new() - } -} -// ANCHOR_END: members - -#[allow(unused_use)] -// ANCHOR: grouped -module book::grouped_imports { - // imports the `new` function and the `Character` struct from - /// the `module_one` module - use book::module_one::{new, Character}; +/// Struct defined in the same module. +public struct Character has drop {} - /// Calls the `new` function from the `module_one` module. - public fun create_character(): Character { - new() - } -} -// ANCHOR_END: grouped - -#[allow(unused_use)] -// ANCHOR: self -module book::self_imports { - // imports the `Character` struct, and the `module_one` module - use book::module_one::{Self, Character}; - - /// Calls the `new` function from the `module_one` module. - public fun create_character(): Character { - module_one::new() - } -} -// ANCHOR_END: self - -#[allow(unused_use)] -// ANCHOR: conflict -module book::conflict_resolution { - // `as` can be placed after any import, including group imports - use book::module_one::{Self as mod, Character as Char}; - - /// Calls the `new` function from the `module_one` module. - public fun create(): Char { - mod::new() - } -} -// ANCHOR_END: conflict - -#[allow(unused_use)] -// ANCHOR: external -module book::imports { - use std::string; // std = 0x1, string is a module in the standard library - use sui::coin; // sui = 0x2, coin is a module in the Sui Framework -} -// ANCHOR_END: external +/// Simple function that creates a new `Character` instance. +public fun new(): Character { Character {} } +// ANCHOR_END: module_one diff --git a/packages/samples/sources/move-basics/module-label.move b/packages/samples/sources/move-basics/module-label.move new file mode 100644 index 00000000..c19cd218 --- /dev/null +++ b/packages/samples/sources/move-basics/module-label.move @@ -0,0 +1,9 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: module +// Module label. +module book::my_module; + +// module body +// ANCHOR_END: module diff --git a/packages/samples/sources/move-basics/module-members.move b/packages/samples/sources/move-basics/module-members.move new file mode 100644 index 00000000..9cc91086 --- /dev/null +++ b/packages/samples/sources/move-basics/module-members.move @@ -0,0 +1,22 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_function, unused_const, unused_use)] +// ANCHOR: members +module book::my_module_with_members; + +// import +use book::my_module; + +// a constant +const CONST: u8 = 0; + +// a struct +public struct Struct {} + +// method alias +public use fun function as Struct.struct_fun; + +// function +fun function(_: &Struct) { /* function body */ } +// ANCHOR_END: members diff --git a/packages/samples/sources/move-basics/module.move b/packages/samples/sources/move-basics/module.move index 40d6506b..6316b67d 100644 --- a/packages/samples/sources/move-basics/module.move +++ b/packages/samples/sources/move-basics/module.move @@ -1,12 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -// ANCHOR: module -module book::my_module { - // module body -} -// ANCHOR_END: module - // ANCHOR: address_literal module 0x0::address_literal { /* ... */ } module book::named_address { /* ... */ } @@ -14,7 +8,7 @@ module book::named_address { /* ... */ } #[allow(unused_function, unused_const, unused_use)] // ANCHOR: members -module book::my_module_with_members { +module book::my_block_module_with_members { // import use book::my_module; @@ -30,4 +24,10 @@ module book::my_module_with_members { // function fun function(_: &Struct) { /* function body */ } } + +// module block allows multiple module definitions in the +// same file but this is not a recommended practice +module book::another_module_in_the_file { + // ... +} // ANCHOR_END: members diff --git a/packages/samples/sources/move-basics/option.move b/packages/samples/sources/move-basics/option.move index e40079ae..5fd24139 100644 --- a/packages/samples/sources/move-basics/option.move +++ b/packages/samples/sources/move-basics/option.move @@ -1,30 +1,29 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +#[allow(unused_variable)] // ANCHOR: registry -module book::user_registry { - use std::string::String; - - /// A struct representing a user record. - public struct User has drop { - first_name: String, - middle_name: Option, - last_name: String, - } - - /// Create a new `User` struct with the given fields. - public fun register( - first_name: String, - middle_name: Option, - last_name: String, - ): User { - User { first_name, middle_name, last_name } - } +module book::user_registry; + +use std::string::String; + +/// A struct representing a user record. +public struct User has drop { + first_name: String, + middle_name: Option, + last_name: String, +} + +/// Create a new `User` struct with the given fields. +public fun register( + first_name: String, + middle_name: Option, + last_name: String, +): User { + User { first_name, middle_name, last_name } } // ANCHOR_END: registry -#[allow(unused_variable)] -module book::option_usage { #[test] fun use_option() { // ANCHOR: usage @@ -32,16 +31,15 @@ module book::option_usage { let mut opt = option::some(b"Alice"); // `option.is_some()` returns true if option contains a value. -assert!(opt.is_some(), 1); +assert!(opt.is_some()); // internal value can be `borrow`ed and `borrow_mut`ed. -assert!(opt.borrow() == &b"Alice", 0); +assert!(opt.borrow() == &b"Alice"); // `option.extract` takes the value out of the option, leaving the option empty. let inner = opt.extract(); // `option.is_none()` returns true if option is None. -assert!(opt.is_none(), 2); +assert!(opt.is_none()); // ANCHOR_END: usage } -} diff --git a/packages/samples/sources/move-basics/references.move b/packages/samples/sources/move-basics/references.move index 91abddff..f2b5e032 100644 --- a/packages/samples/sources/move-basics/references.move +++ b/packages/samples/sources/move-basics/references.move @@ -2,78 +2,77 @@ // SPDX-License-Identifier: Apache-2.0 // ANCHOR: main -module book::references { +module book::references; // ANCHOR: header - /// Error code for when the card is empty. - const ENoUses: u64 = 0; +/// Error code for when the card is empty. +const ENoUses: u64 = 0; - /// Number of uses for a metro pass card. - const USES: u8 = 3; +/// Number of uses for a metro pass card. +const USES: u8 = 3; - /// A metro pass card - public struct Card { uses: u8 } +/// A metro pass card +public struct Card { uses: u8 } // ANCHOR_END: header - // ANCHOR: new - /// Purchase a metro pass card. - public fun purchase(/* pass a Coin */): Card { - Card { uses: USES } - } - // ANCHOR_END: new +// ANCHOR: new +/// Purchase a metro pass card. +public fun purchase(/* pass a Coin */): Card { + Card { uses: USES } +} +// ANCHOR_END: new - // ANCHOR: immutable - /// Show the metro pass card to the inspector. - public fun is_valid(card: &Card): bool { - card.uses > 0 - } - // ANCHOR_END: immutable +// ANCHOR: immutable +/// Show the metro pass card to the inspector. +public fun is_valid(card: &Card): bool { + card.uses > 0 +} +// ANCHOR_END: immutable - // ANCHOR: mutable - /// Use the metro pass card at the turnstile to enter the metro. - public fun enter_metro(card: &mut Card) { - assert!(card.uses > 0, ENoUses); - card.uses = card.uses - 1; - } - // ANCHOR_END: mutable +// ANCHOR: mutable +/// Use the metro pass card at the turnstile to enter the metro. +public fun enter_metro(card: &mut Card) { + assert!(card.uses > 0, ENoUses); + card.uses = card.uses - 1; +} +// ANCHOR_END: mutable - // ANCHOR: move - /// Recycle the metro pass card. - public fun recycle(card: Card) { - assert!(card.uses == 0, ENoUses); - let Card { uses: _ } = card; - } - // ANCHOR_END: move +// ANCHOR: move +/// Recycle the metro pass card. +public fun recycle(card: Card) { + assert!(card.uses == 0, ENoUses); + let Card { uses: _ } = card; +} +// ANCHOR_END: move - // ANCHOR: test - #[test] - fun test_card() { - // declaring variable as mutable because we modify it - let mut card = purchase(); +// ANCHOR: test +#[test] +fun test_card() { + // declaring variable as mutable because we modify it + let mut card = purchase(); - enter_metro(&mut card); + enter_metro(&mut card); - assert!(is_valid(&card), 0); // read the card! + assert!(is_valid(&card)); // read the card! - enter_metro(&mut card); // modify the card but don't move it - enter_metro(&mut card); // modify the card but don't move it + enter_metro(&mut card); // modify the card but don't move it + enter_metro(&mut card); // modify the card but don't move it - recycle(card); // move the card out of the scope - } - // ANCHOR_END: test + recycle(card); // move the card out of the scope +} +// ANCHOR_END: test - // ANCHOR: move_2024 - #[test] - fun test_card_2024() { - // declaring variable as mutable because we modify it - let mut card = purchase(); +// ANCHOR: move_2024 +#[test] +fun test_card_2024() { + // declaring variable as mutable because we modify it + let mut card = purchase(); - card.enter_metro(); // modify the card but don't move it - assert!(card.is_valid(), 0); // read the card! + card.enter_metro(); // modify the card but don't move it + assert!(card.is_valid()); // read the card! - card.enter_metro(); // modify the card but don't move it - card.enter_metro(); // modify the card but don't move it + card.enter_metro(); // modify the card but don't move it + card.enter_metro(); // modify the card but don't move it - card.recycle(); // move the card out of the scope - } - // ANCHOR_END: move_2024 + card.recycle(); // move the card out of the scope } +// ANCHOR_END: move_2024 // ANCHOR_END: main diff --git a/packages/samples/sources/move-basics/string-custom.move b/packages/samples/sources/move-basics/string-custom.move new file mode 100644 index 00000000..40a8edab --- /dev/null +++ b/packages/samples/sources/move-basics/string-custom.move @@ -0,0 +1,2 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 diff --git a/packages/samples/sources/move-basics/string.move b/packages/samples/sources/move-basics/string.move index 83119ffd..f1747c78 100644 --- a/packages/samples/sources/move-basics/string.move +++ b/packages/samples/sources/move-basics/string.move @@ -1,31 +1,31 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +#[allow(unused_variable)] // ANCHOR: custom -module book::custom_string { - /// Anyone can implement a custom string-like type by wrapping a vector. - public struct MyString { - bytes: vector, - } - - /// Implement a `from_bytes` function to convert a vector of bytes to a string. - public fun from_bytes(bytes: vector): MyString { - MyString { bytes } - } - - /// Implement a `bytes` function to convert a string to a vector of bytes. - public fun bytes(self: &MyString): &vector { - &self.bytes - } +module book::custom_string; + +/// Anyone can implement a custom string-like type by wrapping a vector. +public struct MyString { + bytes: vector, +} + +/// Implement a `from_bytes` function to convert a vector of bytes to a string. +public fun from_bytes(bytes: vector): MyString { + MyString { bytes } +} + +/// Implement a `bytes` function to convert a string to a vector of bytes. +public fun bytes(self: &MyString): &vector { + &self.bytes } // ANCHOR_END: custom -module book::string_ascii { - // use std::ascii::String; +// use std::ascii::String; - #[allow(unused_variable)] - #[test] - fun using_strings() { +#[allow(unused_variable)] +#[test] +fun using_strings() { // ANCHOR: ascii // the module is `std::ascii` and the type is `String` use std::ascii::{Self, String}; @@ -38,17 +38,9 @@ let hey: String = ascii::string(b"Hey"); let hey = b"Hey".to_ascii_string(); // ANCHOR_END: ascii - } -} - -module book::string_safe_utf { - } -#[allow(unused_variable)] -module book::string_utf { - #[test] - fun using_strings() { +#[test] fun using_strings_utf8() { // ANCHOR: utf8 // the module is `std::string` and the type is `String` use std::string::{Self, String}; @@ -60,20 +52,18 @@ let hello: String = string::utf8(b"Hello"); // The `.to_string()` alias on the `vector` is more convenient let hello = b"Hello".to_string(); // ANCHOR_END: utf8 - } +} - #[test] - fun safe_strings() { +#[test] fun safe_strings() { // ANCHOR: safe_utf8 // this is a valid UTF-8 string let hello = b"Hello".try_to_string(); -assert!(hello.is_some(), 0); // abort if the value is not valid UTF-8 +assert!(hello.is_some()); // abort if the value is not valid UTF-8 // this is not a valid UTF-8 string let invalid = b"\xFF".try_to_string(); -assert!(invalid.is_none(), 0); // abort if the value is valid UTF-8 +assert!(invalid.is_none()); // abort if the value is valid UTF-8 // ANCHOR_END: safe_utf8 - } } diff --git a/packages/samples/sources/move-basics/struct-methods-2.move b/packages/samples/sources/move-basics/struct-methods-2.move new file mode 100644 index 00000000..4e80f6ec --- /dev/null +++ b/packages/samples/sources/move-basics/struct-methods-2.move @@ -0,0 +1,44 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: hero_and_villain +module book::hero_and_villain; + +/// A struct representing a hero. +public struct Hero has drop { + health: u8, +} + +/// A struct representing a villain. +public struct Villain has drop { + health: u8, +} + +/// Create a new Hero. +public fun new_hero(): Hero { Hero { health: 100 } } + +/// Create a new Villain. +public fun new_villain(): Villain { Villain { health: 100 } } + +// Alias for the `hero_health` method. Will be imported automatically when +// the module is imported. +public use fun hero_health as Hero.health; + +public fun hero_health(hero: &Hero): u8 { hero.health } + +// Alias for the `villain_health` method. Will be imported automatically +// when the module is imported. +public use fun villain_health as Villain.health; + +public fun villain_health(villain: &Villain): u8 { villain.health } + +#[test] +// Test the methods of the `Hero` and `Villain` structs. +fun test_associated_methods() { + let hero = new_hero(); + assert!(hero.health() == 100); + + let villain = new_villain(); + assert!(villain.health() == 100); +} +// ANCHOR_END: hero_and_villain diff --git a/packages/samples/sources/move-basics/struct-methods-3.move b/packages/samples/sources/move-basics/struct-methods-3.move new file mode 100644 index 00000000..d1e9ad7f --- /dev/null +++ b/packages/samples/sources/move-basics/struct-methods-3.move @@ -0,0 +1,28 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: hero_to_bytes +// TODO: better example (external module...) +module book::hero_to_bytes; + +// Alias for the `bcs::to_bytes` method. Imported aliases should be defined +// in the top of the module. +// public use fun bcs::to_bytes as Hero.to_bytes; + +/// A struct representing a hero. +public struct Hero has drop { + health: u8, + mana: u8, +} + +/// Create a new Hero. +public fun new(): Hero { Hero { health: 100, mana: 100 } } + +#[test] +// Test the methods of the `Hero` struct. +fun test_hero_serialize() { + // let mut hero = new(); + // let serialized = hero.to_bytes(); + // assert!(serialized.length() == 3, 1); +} +// ANCHOR_END: hero_to_bytes diff --git a/packages/samples/sources/move-basics/struct-methods.move b/packages/samples/sources/move-basics/struct-methods.move index 7eb51034..a7164ec0 100644 --- a/packages/samples/sources/move-basics/struct-methods.move +++ b/packages/samples/sources/move-basics/struct-methods.move @@ -1,105 +1,37 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 // ANCHOR: hero -module book::hero { - /// A struct representing a hero. - public struct Hero has drop { - health: u8, - mana: u8, - } +module book::hero; - /// Create a new Hero. - public fun new(): Hero { Hero { health: 100, mana: 100 } } - - /// A method which casts a spell, consuming mana. - public fun heal_spell(hero: &mut Hero) { - hero.health = hero.health + 10; - hero.mana = hero.mana - 10; - } - - /// A method which returns the health of the hero. - public fun health(hero: &Hero): u8 { hero.health } - - /// A method which returns the mana of the hero. - public fun mana(hero: &Hero): u8 { hero.mana } - - #[test] - // Test the methods of the `Hero` struct. - fun test_methods() { - let mut hero = new(); - hero.heal_spell(); - - assert!(hero.health() == 110, 1); - assert!(hero.mana() == 90, 2); - } +/// A struct representing a hero. +public struct Hero has drop { + health: u8, + mana: u8, } -// ANCHOR_END: hero - - -// ANCHOR: hero_and_villain -module book::hero_and_villain { - /// A struct representing a hero. - public struct Hero has drop { - health: u8, - } - - /// A struct representing a villain. - public struct Villain has drop { - health: u8, - } - /// Create a new Hero. - public fun new_hero(): Hero { Hero { health: 100 } } +/// Create a new Hero. +public fun new(): Hero { Hero { health: 100, mana: 100 } } - /// Create a new Villain. - public fun new_villain(): Villain { Villain { health: 100 } } - - // Alias for the `hero_health` method. Will be imported automatically when - // the module is imported. - public use fun hero_health as Hero.health; - - public fun hero_health(hero: &Hero): u8 { hero.health } - - // Alias for the `villain_health` method. Will be imported automatically - // when the module is imported. - public use fun villain_health as Villain.health; - - public fun villain_health(villain: &Villain): u8 { villain.health } - - #[test] - // Test the methods of the `Hero` and `Villain` structs. - fun test_associated_methods() { - let hero = new_hero(); - assert!(hero.health() == 100, 1); - - let villain = new_villain(); - assert!(villain.health() == 100, 3); - } +/// A method which casts a spell, consuming mana. +public fun heal_spell(hero: &mut Hero) { + hero.health = hero.health + 10; + hero.mana = hero.mana - 10; } -// ANCHOR_END: hero_and_villain +/// A method which returns the health of the hero. +public fun health(hero: &Hero): u8 { hero.health } -// ANCHOR: hero_to_bytes -// TODO: better example (external module...) -module book::hero_to_bytes { - // Alias for the `bcs::to_bytes` method. Imported aliases should be defined - // in the top of the module. - // public use fun bcs::to_bytes as Hero.to_bytes; +/// A method which returns the mana of the hero. +public fun mana(hero: &Hero): u8 { hero.mana } - /// A struct representing a hero. - public struct Hero has drop { - health: u8, - mana: u8, - } +#[test] +// Test the methods of the `Hero` struct. +fun test_methods() { + let mut hero = new(); + hero.heal_spell(); - /// Create a new Hero. - public fun new(): Hero { Hero { health: 100, mana: 100 } } - - #[test] - // Test the methods of the `Hero` struct. - fun test_hero_serialize() { - // let mut hero = new(); - // let serialized = hero.to_bytes(); - // assert!(serialized.length() == 3, 1); - } + assert!(hero.health() == 110); + assert!(hero.mana() == 90); } -// ANCHOR_END: hero_to_bytes +// ANCHOR_END: hero diff --git a/packages/samples/sources/move-basics/struct.move b/packages/samples/sources/move-basics/struct.move index 97a4da5d..91e2e1f6 100644 --- a/packages/samples/sources/move-basics/struct.move +++ b/packages/samples/sources/move-basics/struct.move @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 #[allow(unused_variable, unused_field)] -module book::struct_syntax { - use std::string::{Self, String}; +module book::struct_syntax; + +use std::string::{Self, String}; // ANCHOR: def /// A struct representing an artist. @@ -35,19 +36,18 @@ let mut artist = Artist { }; // ANCHOR_END: pack - // ANCHOR: access // Access the `name` field of the `Artist` struct. let artist_name = artist.name; // Access a field of the `Artist` struct. -assert!(artist.name == string::utf8(b"The Beatles"), 0); +assert!(artist.name == b"The Beatles".to_string()); // Mutate the `name` field of the `Artist` struct. -artist.name = string::utf8(b"Led Zeppelin"); +artist.name = b"Led Zeppelin".to_string(); // Check that the `name` field has been mutated. -assert!(artist.name == string::utf8(b"Led Zeppelin"), 1); +assert!(artist.name == b"Led Zeppelin".to_string()); // ANCHOR_END: access // ANCHOR: unpack @@ -64,6 +64,4 @@ let artist = Artist { // Unpack the `Artist` struct and ignore the `name` field. let Artist { name: _ } = artist; // ANCHOR_END: unpack_ignore - -} } diff --git a/packages/samples/sources/move-basics/type-reflection.move b/packages/samples/sources/move-basics/type-reflection.move index 02cb7c93..5acfd692 100644 --- a/packages/samples/sources/move-basics/type-reflection.move +++ b/packages/samples/sources/move-basics/type-reflection.move @@ -3,35 +3,35 @@ #[allow(unused_variable)] // ANCHOR: main -module book::type_reflection { - use std::ascii::String; - use std::type_name::{Self, TypeName}; +module book::type_reflection; - /// A function that returns the name of the type `T` and its module and address. - public fun do_i_know_you(): (String, String, String) { - let type_name: TypeName = type_name::get(); +use std::ascii::String; +use std::type_name::{Self, TypeName}; - // there's a way to borrow - let str: &String = type_name.borrow_string(); +/// A function that returns the name of the type `T` and its module and address. +public fun do_i_know_you(): (String, String, String) { + let type_name: TypeName = type_name::get(); - let module_name: String = type_name.get_module(); - let address_str: String = type_name.get_address(); + // there's a way to borrow + let str: &String = type_name.borrow_string(); - // and a way to consume the value - let str = type_name.into_string(); + let module_name: String = type_name.get_module(); + let address_str: String = type_name.get_address(); - (str, module_name, address_str) - } + // and a way to consume the value + let str = type_name.into_string(); - #[test_only] - public struct MyType {} + (str, module_name, address_str) +} + +#[test_only] +public struct MyType {} - #[test] - fun test_type_reflection() { - let (type_name, module_name, _address_str) = do_i_know_you(); +#[test] +fun test_type_reflection() { + let (type_name, module_name, _address_str) = do_i_know_you(); - // - assert!(module_name == b"type_reflection".to_ascii_string(), 1); - } + // + assert!(module_name == b"type_reflection".to_ascii_string()); } // ANCHOR_END: main diff --git a/packages/samples/sources/move-basics/vector.move b/packages/samples/sources/move-basics/vector.move index 1a092561..4fba05be 100644 --- a/packages/samples/sources/move-basics/vector.move +++ b/packages/samples/sources/move-basics/vector.move @@ -4,7 +4,6 @@ #[allow(unused_variable)] module book::vector_syntax { #[test] fun test_vector() { - // ANCHOR: literal // An empty vector of bool elements. let empty: vector = vector[]; @@ -21,37 +20,35 @@ let vv: vector> = vector[ } #[test] fun vector_methods() { - // ANCHOR: methods let mut v = vector[10u8, 20, 30]; -assert!(v.length() == 3, 0); -assert!(!v.is_empty(), 1); +assert!(v.length() == 3); +assert!(!v.is_empty()); v.push_back(40); let last_value = v.pop_back(); -assert!(last_value == 40, 2); +assert!(last_value == 40); // ANCHOR_END: methods } - } module book::non_droppable_vec { // ANCHOR: no_drop - /// A struct without `drop` ability. - public struct NoDrop {} - - #[test] - fun test_destroy_empty() { - // Initialize a vector of `NoDrop` elements. - let v = vector[]; - - // While we know that `v` is empty, we still need to call - // the explicit `destroy_empty` function to discard the vector. - v.destroy_empty(); - } +/// A struct without `drop` ability. +public struct NoDrop {} + +#[test] +fun test_destroy_empty() { + // Initialize a vector of `NoDrop` elements. + let v = vector[]; + + // While we know that `v` is empty, we still need to call + // the explicit `destroy_empty` function to discard the vector. + v.destroy_empty(); +} // ANCHOR_END: no_drop } diff --git a/packages/samples/sources/programmability/bcs.move b/packages/samples/sources/programmability/bcs.move index 81565d85..17fd7dbd 100644 --- a/packages/samples/sources/programmability/bcs.move +++ b/packages/samples/sources/programmability/bcs.move @@ -47,7 +47,7 @@ custom_bytes.append(bcs::to_bytes(&b"hello, world!".to_string())); custom_bytes.append(bcs::to_bytes(&true)); // struct is just a sequence of fields, so the bytes should be the same! -assert!(&struct_bytes == &custom_bytes, 0); +assert!(&struct_bytes == &custom_bytes); // ANCHOR_END: encode_struct } @@ -61,12 +61,12 @@ let mut bcs = bcs::new(x"010000000000000000"); // Same bytes can be read differently, for example: Option let value: Option = bcs.peel_option_u64(); -assert!(value.is_some(), 0); -assert!(value.borrow() == &0, 1); +assert!(value.is_some()); +assert!(value.borrow() == &0); let remainder = bcs.into_remainder_bytes(); -assert!(remainder.length() == 0, 2); +assert!(remainder.length() == 0); // ANCHOR_END: decode // ANCHOR: chain_decode @@ -94,25 +94,25 @@ while (len > 0) { len = len - 1; }; -assert!(vec.length() == 1, 0); +assert!(vec.length() == 1); // ANCHOR_END: decode_vector // ANCHOR: decode_option let mut bcs = bcs::new(x"00"); let is_some = bcs.peel_bool(); -assert!(is_some == false, 0); +assert!(is_some == false); let mut bcs = bcs::new(x"0101"); let is_some = bcs.peel_bool(); let value = bcs.peel_u8(); -assert!(is_some == true, 1); -assert!(value == 1, 2); +assert!(is_some == true); +assert!(value == 1); // ANCHOR_END: decode_option // ANCHOR: decode_struct -// some bytes... +// some bytes... let mut bcs = bcs::new(x"0101010F0000000000F00000000000"); let (age, is_active, name) = ( diff --git a/packages/samples/sources/programmability/capability-2.move b/packages/samples/sources/programmability/capability-2.move new file mode 100644 index 00000000..b5a6250a --- /dev/null +++ b/packages/samples/sources/programmability/capability-2.move @@ -0,0 +1,19 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: admin_cap +module book::admin_cap; + +/// The capability granting the admin privileges in the system. +/// Created only once in the `init` function. +public struct AdminCap has key { id: UID } + +/// Create the AdminCap object on package publish and transfer it to the +/// package owner. +fun init(ctx: &mut TxContext) { + transfer::transfer( + AdminCap { id: object::new(ctx) }, + ctx.sender() + ) +} +// ANCHOR_END: admin_cap diff --git a/packages/samples/sources/programmability/capability-3.move b/packages/samples/sources/programmability/capability-3.move new file mode 100644 index 00000000..d2b8a2cd --- /dev/null +++ b/packages/samples/sources/programmability/capability-3.move @@ -0,0 +1,21 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module book::addr_vs_cap; + +public struct User has key, store { id: UID } + +// ANCHOR: with_address +/// Error code for unauthorized access. +const ENotAuthorized: u64 = 0; + +/// The application admin address. +const APPLICATION_ADMIN: address = @0xa11ce; + +/// Creates a new user in the system. Requires the sender to be the application +/// admin. +public fun new(ctx: &mut TxContext): User { + assert!(ctx.sender() == APPLICATION_ADMIN, ENotAuthorized); + User { id: object::new(ctx) } +} +// ANCHOR_END: with_address diff --git a/packages/samples/sources/programmability/capability-4.move b/packages/samples/sources/programmability/capability-4.move new file mode 100644 index 00000000..2ba47cd1 --- /dev/null +++ b/packages/samples/sources/programmability/capability-4.move @@ -0,0 +1,17 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module book::cap_vs_addr; + +public struct User has key, store { id: UID } + +// ANCHOR: with_capability +/// Grants the owner the right to create new users in the system. +public struct AdminCap {} + +/// Creates a new user in the system. Requires the `AdminCap` capability to be +/// passed as the first argument. +public fun new(_: &AdminCap, ctx: &mut TxContext): User { + User { id: object::new(ctx) } +} +// ANCHOR_END: with_capability diff --git a/packages/samples/sources/programmability/capability.move b/packages/samples/sources/programmability/capability.move index 00a8e1b0..f81ca273 100644 --- a/packages/samples/sources/programmability/capability.move +++ b/packages/samples/sources/programmability/capability.move @@ -2,98 +2,43 @@ // SPDX-License-Identifier: Apache-2.0 // ANCHOR: main -module book::capability { - use std::string::String; - use sui::event; +module book::capability; - /// The capability granting the application admin the right to create new - /// accounts in the system. - public struct AdminCap has key, store { id: UID } +use std::string::String; +use sui::event; - /// The user account in the system. - public struct Account has key, store { - id: UID, - name: String - } - - /// A simple `Ping` event with no data. - public struct Ping has copy, drop { by: ID } - - /// Creates a new account in the system. Requires the `AdminCap` capability - /// to be passed as the first argument. - public fun new(_: &AdminCap, name: String, ctx: &mut TxContext): Account { - Account { - id: object::new(ctx), - name, - } - } - - /// Account, and any other objects, can also be used as a Capability in the - /// application. For example, to emit an event. - public fun send_ping(acc: &Account) { - event::emit(Ping { - by: acc.id.to_inner() - }) - } +/// The capability granting the application admin the right to create new +/// accounts in the system. +public struct AdminCap has key, store { id: UID } - /// Updates the account name. Can only be called by the `Account` owner. - public fun update(account: &mut Account, name: String) { - account.name = name; - } +/// The user account in the system. +public struct Account has key, store { + id: UID, + name: String } -// ANCHOR_END: main -// ANCHOR: admin_cap -module book::admin_cap { - /// The capability granting the admin privileges in the system. - /// Created only once in the `init` function. - public struct AdminCap has key { id: UID } +/// A simple `Ping` event with no data. +public struct Ping has copy, drop { by: ID } - /// Create the AdminCap object on package publish and transfer it to the - /// package owner. - fun init(ctx: &mut TxContext) { - transfer::transfer( - AdminCap { id: object::new(ctx) }, - ctx.sender() - ) +/// Creates a new account in the system. Requires the `AdminCap` capability +/// to be passed as the first argument. +public fun new(_: &AdminCap, name: String, ctx: &mut TxContext): Account { + Account { + id: object::new(ctx), + name, } } -// ANCHOR_END: admin_cap - - -module book::addr_vs_cap { - - public struct User has key, store { id: UID } - -// ANCHOR: with_address -/// Error code for unauthorized access. -const ENotAuthorized: u64 = 0; - -/// The application admin address. -const APPLICATION_ADMIN: address = @0xa11ce; - -/// Creates a new user in the system. Requires the sender to be the application -/// admin. -public fun new(ctx: &mut TxContext): User { - assert!(ctx.sender() == APPLICATION_ADMIN, ENotAuthorized); - User { id: object::new(ctx) } -} -// ANCHOR_END: with_address +/// Account, and any other objects, can also be used as a Capability in the +/// application. For example, to emit an event. +public fun send_ping(acc: &Account) { + event::emit(Ping { + by: acc.id.to_inner() + }) } -module book::cap_vs_addr { - - public struct User has key, store { id: UID } - -// ANCHOR: with_capability -/// Grants the owner the right to create new users in the system. -public struct AdminCap {} - -/// Creates a new user in the system. Requires the `AdminCap` capability to be -/// passed as the first argument. -public fun new(_: &AdminCap, ctx: &mut TxContext): User { - User { id: object::new(ctx) } -} -// ANCHOR_END: with_capability +/// Updates the account name. Can only be called by the `Account` owner. +public fun update(account: &mut Account, name: String) { + account.name = name; } +// ANCHOR_END: main diff --git a/packages/samples/sources/programmability/collections-2.move b/packages/samples/sources/programmability/collections-2.move new file mode 100644 index 00000000..d06ce27b --- /dev/null +++ b/packages/samples/sources/programmability/collections-2.move @@ -0,0 +1,29 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_variable, unused_field)] +// ANCHOR: vec_set +module book::collections_vec_set; + +use sui::vec_set::{Self, VecSet}; + +public struct App has drop { + /// `VecSet` used in the struct definition + subscribers: VecSet
+} + +#[test] +fun vec_set_playground() { + let set = vec_set::empty(); // create an empty set + let mut set = vec_set::singleton(1); // create a set with a single item + + set.insert(2); // add an item to the set + set.insert(3); + + assert!(set.contains(&1)); // check if an item is in the set + assert!(set.size() == 3); // get the number of items in the set + assert!(!set.is_empty()); // check if the set is empty + + set.remove(&2); // remove an item from the set +} +// ANCHOR_END: vec_set diff --git a/packages/samples/sources/programmability/collections-3.move b/packages/samples/sources/programmability/collections-3.move new file mode 100644 index 00000000..35e7066b --- /dev/null +++ b/packages/samples/sources/programmability/collections-3.move @@ -0,0 +1,28 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_field, unused_variable)] +// ANCHOR: vec_map +module book::collections_vec_map; + +use std::string::String; +use sui::vec_map::{Self, VecMap}; + +public struct Metadata has drop { + name: String, + /// `VecMap` used in the struct definition + attributes: VecMap +} + +#[test] +fun vec_map_playground() { + let mut map = vec_map::empty(); // create an empty map + + map.insert(2, b"two".to_string()); // add a key-value pair to the map + map.insert(3, b"three".to_string()); + + assert!(map.contains(&2)); // check if a key is in the map + + map.remove(&2); // remove a key-value pair from the map +} +// ANCHOR_END: vec_map diff --git a/packages/samples/sources/programmability/collections-4.move b/packages/samples/sources/programmability/collections-4.move new file mode 100644 index 00000000..1050d165 --- /dev/null +++ b/packages/samples/sources/programmability/collections-4.move @@ -0,0 +1,22 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused_field, unused_variable, lint(collection_equality))] +module book::collections_compare_vec_set; + +use sui::vec_set; + +#[test, expected_failure] +fun test_compare() { +// ANCHOR: vec_set_comparison +let mut set1 = vec_set::empty(); +set1.insert(1); +set1.insert(2); + +let mut set2 = vec_set::empty(); +set2.insert(2); +set2.insert(1); + +assert!(set1 == set2); // aborts! +// ANCHOR_END: vec_set_comparison +} diff --git a/packages/samples/sources/programmability/collections.move b/packages/samples/sources/programmability/collections.move index 086eb46c..ed51ecea 100644 --- a/packages/samples/sources/programmability/collections.move +++ b/packages/samples/sources/programmability/collections.move @@ -3,91 +3,19 @@ #[allow(unused_variable, unused_field)] // ANCHOR: vector -module book::collections_vector { - use std::string::String; +module book::collections_vector; - /// The Book that can be sold by a `BookStore` - public struct Book has key, store { - id: UID, - name: String - } +use std::string::String; - /// The BookStore that sells `Book`s - public struct BookStore has key, store { - id: UID, - books: vector - } +/// The Book that can be sold by a `BookStore` +public struct Book has key, store { + id: UID, + name: String } -// ANCHOR_END: vector - -#[allow(unused_variable, unused_field)] -// ANCHOR: vec_set -module book::collections_vec_set { - use sui::vec_set::{Self, VecSet}; - - public struct App has drop { - /// `VecSet` used in the struct definition - subscribers: VecSet
- } - - #[test] - fun vec_set_playground() { - let set = vec_set::empty(); // create an empty set - let mut set = vec_set::singleton(1); // create a set with a single item - - set.insert(2); // add an item to the set - set.insert(3); - - assert!(set.contains(&1), 0); // check if an item is in the set - assert!(set.size() == 3, 1); // get the number of items in the set - assert!(!set.is_empty(), 2); // check if the set is empty - - set.remove(&2); // remove an item from the set - } -} -// ANCHOR_END: vec_set - -#[allow(unused_field, unused_variable)] -// ANCHOR: vec_map -module book::collections { - use std::string::String; - use sui::vec_map::{Self, VecMap}; - public struct Metadata has drop { - name: String, - /// `VecMap` used in the struct definition - attributes: VecMap - } - - #[test] - fun vec_map_playground() { - let mut map = vec_map::empty(); // create an empty map - - map.insert(2, b"two".to_string()); // add a key-value pair to the map - map.insert(3, b"three".to_string()); - - assert!(map.contains(&2), 0); // check if a key is in the map - - map.remove(&2); // remove a key-value pair from the map - } -} -// ANCHOR_END: vec_map - -#[allow(unused_field, unused_variable, lint(collection_equality))] -module book::collections_compare_vec_set { -use sui::vec_set; -#[test, expected_failure] -fun test_compare() { -// ANCHOR: vec_set_comparison -let mut set1 = vec_set::empty(); -set1.insert(1); -set1.insert(2); - -let mut set2 = vec_set::empty(); -set2.insert(2); -set2.insert(1); - -assert!(set1 == set2, 0); -// ANCHOR_END: vec_set_comparison -} +/// The BookStore that sells `Book`s +public struct BookStore has key, store { + id: UID, + books: vector } +// ANCHOR_END: vector diff --git a/packages/samples/sources/programmability/display.move b/packages/samples/sources/programmability/display.move index eb7a2ca9..07f3f9a0 100644 --- a/packages/samples/sources/programmability/display.move +++ b/packages/samples/sources/programmability/display.move @@ -1,9 +1,59 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 + #[allow(unused_field)] -module book::background { - use std::string::String; +// ANCHOR: hero +module book::arena; + +use std::string::String; +use sui::package; +use sui::display; + +/// The One Time Witness to claim the `Publisher` object. +public struct ARENA has drop {} + +/// Some object which will be displayed. +public struct Hero has key { + id: UID, + class: String, + level: u64, +} + +/// In the module initializer we create the `Publisher` object, and then +/// the Display for the `Hero` type. +fun init(otw: ARENA, ctx: &mut TxContext) { + let publisher = package::claim(otw, ctx); + let mut display = display::new(&publisher, ctx); + + display.add( + b"name".to_string(), + b"{class} (lvl. {level})".to_string() + ); + + display.add( + b"description".to_string(), + b"One of the greatest heroes of all time. Join us!".to_string() + ); + + display.add( + b"link".to_string(), + b"https://example.com/hero/{id}".to_string() + ); + + display.add( + b"image_url".to_string(), + b"https://example.com/hero/{class}.jpg".to_string() + ); + + // Update the display with the new data. + // Must be called to apply changes. + display.update_version(); + + transfer::public_transfer(publisher, ctx.sender()); + transfer::public_transfer(display, ctx.sender()); +} +// ANCHOR_END: hero // ANCHOR: background /// An attempt to standardize the object structure for display. @@ -20,64 +70,6 @@ public struct CounterWithDisplay has key { // ... } // ANCHOR_END: background -} - -#[allow(unused_field)] -// ANCHOR: hero -module book::arena { - use std::string::String; - use sui::package; - use sui::display; - - /// The One Time Witness to claim the `Publisher` object. - public struct ARENA has drop {} - - /// Some object which will be displayed. - public struct Hero has key { - id: UID, - class: String, - level: u64, - } - - /// In the module initializer we create the `Publisher` object, and then - /// the Display for the `Hero` type. - fun init(otw: ARENA, ctx: &mut TxContext) { - let publisher = package::claim(otw, ctx); - let mut display = display::new(&publisher, ctx); - - display.add( - b"name".to_string(), - b"{class} (lvl. {level})".to_string() - ); - - display.add( - b"description".to_string(), - b"One of the greatest heroes of all time. Join us!".to_string() - ); - - display.add( - b"link".to_string(), - b"https://example.com/hero/{id}".to_string() - ); - - display.add( - b"image_url".to_string(), - b"https://example.com/hero/{class}.jpg".to_string() - ); - - // Update the display with the new data. - // Must be called to apply changes. - display.update_version(); - - transfer::public_transfer(publisher, ctx.sender()); - transfer::public_transfer(display, ctx.sender()); - } -} -// ANCHOR_END: hero - -#[allow(unused_field)] -module book::nested_metadata { - use std::string::String; // ANCHOR: nested /// Some common metadata for objects. @@ -94,5 +86,3 @@ public struct LittlePony has key, store { metadata: Metadata } // ANCHOR_END: nested - -} diff --git a/packages/samples/sources/programmability/dynamic-fields.move b/packages/samples/sources/programmability/dynamic-fields.move index 9169bae8..b0e11bfe 100644 --- a/packages/samples/sources/programmability/dynamic-fields.move +++ b/packages/samples/sources/programmability/dynamic-fields.move @@ -2,62 +2,63 @@ // SPDX-License-Identifier: Apache-2.0 // ANCHOR: usage -module book::dynamic_collection { - // a very common alias for `dynamic_field` is `df` since the - // module name is quite long - use sui::dynamic_field as df; - use std::string::String; - - /// The object that we will attach dynamic fields to. - public struct Character has key { - id: UID - } - - // List of different accessories that can be attached to a character. - // They must have the `store` ability. - public struct Hat has key, store { id: UID, color: u32 } - public struct Mustache has key, store { id: UID } - - #[test] - fun test_character_and_accessories() { - let ctx = &mut tx_context::dummy(); - let mut character = Character { id: object::new(ctx) }; - - // Attach a hat to the character's UID - df::add( - &mut character.id, - b"hat_key", - Hat { id: object::new(ctx), color: 0xFF0000 } - ); - - // Similarly, attach a mustache to the character's UID - df::add( - &mut character.id, - b"mustache_key", - Mustache { id: object::new(ctx) } - ); - - // Check that the hat and mustache are attached to the character - // - assert!(df::exists_(&character.id, b"hat_key"), 0); - assert!(df::exists_(&character.id, b"mustache_key"), 1); - - // Modify the color of the hat - let hat: &mut Hat = df::borrow_mut(&mut character.id, b"hat_key"); - hat.color = 0x00FF00; - - // Remove the hat and mustache from the character - let hat: Hat = df::remove(&mut character.id, b"hat_key"); - let mustache: Mustache = df::remove(&mut character.id, b"mustache_key"); - - // Check that the hat and mustache are no longer attached to the character - assert!(!df::exists_(&character.id, b"hat_key"), 0); - assert!(!df::exists_(&character.id, b"mustache_key"), 1); - - sui::test_utils::destroy(character); - sui::test_utils::destroy(mustache); - sui::test_utils::destroy(hat); - } +module book::dynamic_fields; + +// a very common alias for `dynamic_field` is `df` since the +// module name is quite long +use sui::dynamic_field as df; +use std::string::String; + +/// The object that we will attach dynamic fields to. +public struct Character has key { + id: UID +} + +// List of different accessories that can be attached to a character. +// They must have the `store` ability. +public struct Hat has key, store { id: UID, color: u32 } +public struct Mustache has key, store { id: UID } + +#[test] +fun test_character_and_accessories() { + let ctx = &mut tx_context::dummy(); + let mut character = Character { id: object::new(ctx) }; + + // Attach a hat to the character's UID + df::add( + &mut character.id, + b"hat_key", + Hat { id: object::new(ctx), color: 0xFF0000 } + ); + + // Similarly, attach a mustache to the character's UID + df::add( + &mut character.id, + b"mustache_key", + Mustache { id: object::new(ctx) } + ); + + // Check that the hat and mustache are attached to the character + // + assert!(df::exists_(&character.id, b"hat_key"), 0); + assert!(df::exists_(&character.id, b"mustache_key"), 1); + + // Modify the color of the hat + let hat: &mut Hat = df::borrow_mut(&mut character.id, b"hat_key"); + hat.color = 0x00FF00; + + // Remove the hat and mustache from the character + let hat: Hat = df::remove(&mut character.id, b"hat_key"); + let mustache: Mustache = df::remove(&mut character.id, b"mustache_key"); + + // Check that the hat and mustache are no longer attached to the character + assert!(!df::exists_(&character.id, b"hat_key"), 0); + assert!(!df::exists_(&character.id, b"mustache_key"), 1); + + sui::test_utils::destroy(character); + sui::test_utils::destroy(mustache); + sui::test_utils::destroy(hat); +} // ANCHOR_END: usage @@ -150,4 +151,3 @@ df::add(&mut character.id, MetadataKey {}, 42); // ANCHOR_END: custom_type_usage sui::test_utils::destroy(character); } -} diff --git a/packages/samples/sources/programmability/dynamic-object-fields.move b/packages/samples/sources/programmability/dynamic-object-fields.move index cf59d677..3b99a118 100644 --- a/packages/samples/sources/programmability/dynamic-object-fields.move +++ b/packages/samples/sources/programmability/dynamic-object-fields.move @@ -3,48 +3,48 @@ #[allow(unused_variable)] // ANCHOR: usage -module book::dynamic_object_field { - use std::string::String; +module book::dynamic_object_field; - // there are two common aliases for the long module name: `dof` and - // `ofield`. Both are commonly used and met in different projects. - use sui::dynamic_object_field as dof; - use sui::dynamic_field as df; +use std::string::String; - /// The `Character` that we will use for the example - public struct Character has key { id: UID } +// there are two common aliases for the long module name: `dof` and +// `ofield`. Both are commonly used and met in different projects. +use sui::dynamic_object_field as dof; +use sui::dynamic_field as df; - /// Metadata that doesn't have the `key` ability - public struct Metadata has store, drop { name: String } +/// The `Character` that we will use for the example +public struct Character has key { id: UID } - /// Accessory that has the `key` and `store` abilities. - public struct Accessory has key, store { id: UID } +/// Metadata that doesn't have the `key` ability +public struct Metadata has store, drop { name: String } - #[test] - fun equip_accessory() { - let ctx = &mut tx_context::dummy(); - let mut character = Character { id: object::new(ctx) }; +/// Accessory that has the `key` and `store` abilities. +public struct Accessory has key, store { id: UID } - // Create an accessory and attach it to the character - let hat = Accessory { id: object::new(ctx) }; +#[test] +fun equip_accessory() { + let ctx = &mut tx_context::dummy(); + let mut character = Character { id: object::new(ctx) }; - // Add the hat to the character. Just like with `dynamic_fields` - dof::add(&mut character.id, b"hat_key", hat); + // Create an accessory and attach it to the character + let hat = Accessory { id: object::new(ctx) }; - // However for non-key structs we can only use `dynamic_field` - df::add(&mut character.id, b"metadata_key", Metadata { - name: b"John".to_string() - }); + // Add the hat to the character. Just like with `dynamic_fields` + dof::add(&mut character.id, b"hat_key", hat); - // Borrow the hat from the character - let hat_id = dof::id(&character.id, b"hat_key").extract(); // Option - let hat_ref: &Accessory = dof::borrow(&character.id, b"hat_key"); - let hat_mut: &mut Accessory = dof::borrow_mut(&mut character.id, b"hat_key"); - let hat: Accessory = dof::remove(&mut character.id, b"hat_key"); + // However for non-key structs we can only use `dynamic_field` + df::add(&mut character.id, b"metadata_key", Metadata { + name: b"John".to_string() + }); - // Clean up, Metadata is an orphan now. - sui::test_utils::destroy(hat); - sui::test_utils::destroy(character); - } + // Borrow the hat from the character + let hat_id = dof::id(&character.id, b"hat_key").extract(); // Option + let hat_ref: &Accessory = dof::borrow(&character.id, b"hat_key"); + let hat_mut: &mut Accessory = dof::borrow_mut(&mut character.id, b"hat_key"); + let hat: Accessory = dof::remove(&mut character.id, b"hat_key"); + + // Clean up, Metadata is an orphan now. + sui::test_utils::destroy(hat); + sui::test_utils::destroy(character); } // ANCHOR_END: usage diff --git a/packages/samples/sources/programmability/events.move b/packages/samples/sources/programmability/events.move index f9508205..ba4d5977 100644 --- a/packages/samples/sources/programmability/events.move +++ b/packages/samples/sources/programmability/events.move @@ -2,33 +2,33 @@ // SPDX-License-Identifier: Apache-2.0 // ANCHOR: emit -module book::events { - use sui::coin::Coin; - use sui::sui::SUI; - use sui::event; +module book::events; - /// The item that can be purchased. - public struct Item has key { id: UID } +use sui::coin::Coin; +use sui::sui::SUI; +use sui::event; - /// Event emitted when an item is purchased. Contains the ID of the item and - /// the price for which it was purchased. - public struct ItemPurchased has copy, drop { - item: ID, - price: u64 - } +/// The item that can be purchased. +public struct Item has key { id: UID } - /// A marketplace function which performs the purchase of an item. - public fun purchase(coin: Coin, ctx: &mut TxContext) { - let item = Item { id: object::new(ctx) }; +/// Event emitted when an item is purchased. Contains the ID of the item and +/// the price for which it was purchased. +public struct ItemPurchased has copy, drop { + item: ID, + price: u64 +} + +/// A marketplace function which performs the purchase of an item. +public fun purchase(coin: Coin, ctx: &mut TxContext) { + let item = Item { id: object::new(ctx) }; - // Create an instance of `ItemPurchased` and pass it to `event::emit`. - event::emit(ItemPurchased { - item: object::id(&item), - price: coin.value() - }); + // Create an instance of `ItemPurchased` and pass it to `event::emit`. + event::emit(ItemPurchased { + item: object::id(&item), + price: coin.value() + }); - // Omitting the rest of the implementation to keep the example simple. - abort 0 - } + // Omitting the rest of the implementation to keep the example simple. + abort 0 } // ANCHOR_END: emit diff --git a/packages/samples/sources/programmability/fast-path.move b/packages/samples/sources/programmability/fast-path.move index 78b302cf..6afa71ca 100644 --- a/packages/samples/sources/programmability/fast-path.move +++ b/packages/samples/sources/programmability/fast-path.move @@ -36,7 +36,7 @@ module book::coffee_machine { /// Put the cup back. This is a fast path operation. public fun put_back(cup: Cup) { let Cup { id, has_coffee: _ } = cup; - object::delete(id); + id.delete(); } } // ANCHOR_END: main diff --git a/packages/samples/sources/programmability/module-initializer-2.move b/packages/samples/sources/programmability/module-initializer-2.move new file mode 100644 index 00000000..abb16aec --- /dev/null +++ b/packages/samples/sources/programmability/module-initializer-2.move @@ -0,0 +1,18 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// ANCHOR: other +// In the same package as the `shop` module +module book::bank; + +public struct Bank has key { + id: UID, + /* ... */ +} + +fun init(ctx: &mut TxContext) { + transfer::share_object(Bank { + id: object::new(ctx) + }); +} +// ANCHOR_END: other diff --git a/packages/samples/sources/programmability/module-initializer.move b/packages/samples/sources/programmability/module-initializer.move index a5ca3dde..fb433868 100644 --- a/packages/samples/sources/programmability/module-initializer.move +++ b/packages/samples/sources/programmability/module-initializer.move @@ -2,46 +2,29 @@ // SPDX-License-Identifier: Apache-2.0 // ANCHOR: main -module book::shop { - /// The Capability which grants the Shop owner the right to manage - /// the shop. - public struct ShopOwnerCap has key, store { id: UID } +module book::shop; - /// The singular Shop itself, created in the `init` function. - public struct Shop has key { - id: UID, - /* ... */ - } +/// The Capability which grants the Shop owner the right to manage +/// the shop. +public struct ShopOwnerCap has key, store { id: UID } - // Called only once, upon module publication. It must be - // private to prevent external invocation. - fun init(ctx: &mut TxContext) { - // Transfers the ShopOwnerCap to the sender (publisher). - transfer::transfer(ShopOwnerCap { - id: object::new(ctx) - }, ctx.sender()); - - // Shares the Shop object. - transfer::share_object(Shop { - id: object::new(ctx) - }); - } +/// The singular Shop itself, created in the `init` function. +public struct Shop has key { + id: UID, + /* ... */ } -// ANCHOR_END: main -// ANCHOR: other -// In the same package as the `shop` module -module book::bank { +// Called only once, upon module publication. It must be +// private to prevent external invocation. +fun init(ctx: &mut TxContext) { + // Transfers the ShopOwnerCap to the sender (publisher). + transfer::transfer(ShopOwnerCap { + id: object::new(ctx) + }, ctx.sender()); - public struct Bank has key { - id: UID, - /* ... */ - } - - fun init(ctx: &mut TxContext) { - transfer::share_object(Bank { - id: object::new(ctx) - }); - } + // Shares the Shop object. + transfer::share_object(Shop { + id: object::new(ctx) + }); } -// ANCHOR_END: other +// ANCHOR_END: main diff --git a/packages/samples/sources/programmability/one-time-witness.move b/packages/samples/sources/programmability/one-time-witness.move index af98a1e9..3325e508 100644 --- a/packages/samples/sources/programmability/one-time-witness.move +++ b/packages/samples/sources/programmability/one-time-witness.move @@ -3,19 +3,18 @@ #[allow(unused_variable, unused_mut_parameter)] // ANCHOR: definition -module book::one_time { - /// The OTW for the `book::one_time` module. - /// Only `drop`, no fields, no generics, all uppercase. - public struct ONE_TIME has drop {} +module book::one_time; - /// Receive the instance of `ONE_TIME` as the first argument. - fun init(otw: ONE_TIME, ctx: &mut TxContext) { - // do something with the OTW - } +/// The OTW for the `book::one_time` module. +/// Only `drop`, no fields, no generics, all uppercase. +public struct ONE_TIME has drop {} + +/// Receive the instance of `ONE_TIME` as the first argument. +fun init(otw: ONE_TIME, ctx: &mut TxContext) { + // do something with the OTW } // ANCHOR_END: definition -module book::one_time_usage { // ANCHOR: usage use sui::types; @@ -26,4 +25,3 @@ public fun takes_witness(otw: T) { assert!(types::is_one_time_witness(&otw), ENotOneTimeWitness); } // ANCHOR_END: usage -} diff --git a/packages/samples/sources/programmability/publisher.move b/packages/samples/sources/programmability/publisher.move index e6365ce7..1af4382a 100644 --- a/packages/samples/sources/programmability/publisher.move +++ b/packages/samples/sources/programmability/publisher.move @@ -1,35 +1,32 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +#[allow(unused_variable)] // ANCHOR: publisher -module book::publisher { - /// Some type defined in the module. - public struct Book {} - - /// The OTW for the module. - public struct PUBLISHER has drop {} - - /// Uses the One Time Witness to claim the Publisher object. - fun init(otw: PUBLISHER, ctx: &mut TxContext) { - // Claim the Publisher object. - let publisher = sui::package::claim(otw, ctx); - - // Usually it is transferred to the sender. - // It can also be stored in another object. - transfer::public_transfer(publisher, ctx.sender()) - } -} -// ANCHOR_END: publisher +module book::publisher; -#[allow(unused_variable)] -module book::use_publisher { - use sui::package::{Self, Publisher}; +use sui::package::{Self, Publisher}; + +/// Some type defined in the module. +public struct Book {} - public struct Book {} +/// The OTW for the module. +public struct PUBLISHER has drop {} - public struct USE_PUBLISHER has drop {} +/// Uses the One Time Witness to claim the Publisher object. +fun init(otw: PUBLISHER, ctx: &mut TxContext) { + // Claim the Publisher object. + let publisher: Publisher = sui::package::claim(otw, ctx); - const ENotAuthorized: u64 = 1; + // Usually it is transferred to the sender. + // It can also be stored in another object. + transfer::public_transfer(publisher, ctx.sender()) +} +// ANCHOR_END: publisher + +public struct USE_PUBLISHER has drop {} + +const ENotAuthorized: u64 = 1; #[test] @@ -39,11 +36,11 @@ let publisher = package::test_claim(USE_PUBLISHER {}, ctx); // ANCHOR: use_publisher // Checks if the type is from the same module, hence the `Publisher` has the // authority over it. -assert!(publisher.from_module(), 0); +assert!(publisher.from_module()); // Checks if the type is from the same package, hence the `Publisher` has the // authority over it. -assert!(publisher.from_package(), 0); +assert!(publisher.from_package()); // ANCHOR_END: use_publisher sui::test_utils::destroy(publisher); } @@ -56,4 +53,3 @@ public fun admin_action(cap: &Publisher, /* app objects... */ param: u64) { // perform application-specific action } // ANCHOR_END: publisher_as_admin -} diff --git a/packages/samples/sources/programmability/transaction-context.move b/packages/samples/sources/programmability/transaction-context.move index d8a4ce85..373f554a 100644 --- a/packages/samples/sources/programmability/transaction-context.move +++ b/packages/samples/sources/programmability/transaction-context.move @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #[allow(unused_variable)] -module book::transaction_context { +module book::transaction_context; // ANCHOR: reading public fun some_action(ctx: &TxContext) { @@ -12,5 +12,3 @@ public fun some_action(ctx: &TxContext) { // ... } // ANCHOR_END: reading - -} diff --git a/packages/samples/sources/programmability/witness-pattern.move b/packages/samples/sources/programmability/witness-pattern.move index 619ddb6f..45bbdb97 100644 --- a/packages/samples/sources/programmability/witness-pattern.move +++ b/packages/samples/sources/programmability/witness-pattern.move @@ -1,14 +1,13 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -module book::witness_definition { +module book::witness_definition; + // ANCHOR: definition /// Canonical definition of a witness - a type with the `drop` ability. public struct MyWitness has drop {} // ANCHOR_END: definition -} -module book::regulated_coin { // ANCHOR: regulated_coin /// A custom RegulatedCoin type with implementable functions. public struct RegulatedCoin has key { @@ -42,4 +41,3 @@ public fun join(coin: &mut RegulatedCoin, other: RegulatedCoin) { id.delete(); } // ANCHOR_END: regulated_coin -} diff --git a/packages/samples/sources/your-first-move/hello_world.move b/packages/samples/sources/your-first-move/hello_world.move index f852b18c..0f3ae515 100644 --- a/packages/samples/sources/your-first-move/hello_world.move +++ b/packages/samples/sources/your-first-move/hello_world.move @@ -1,16 +1,16 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -module book::hello_world { - use std::string::String; +module book::hello_world; - public fun hello_world(): String { - b"Hello, World!".to_string() - } +use std::string::String; - #[test] - fun test_is_hello_world() { - let expected = b"Hello, World!".to_string(); - assert!(hello_world() == expected, 0) - } +public fun hello_world(): String { + b"Hello, World!".to_string() +} + +#[test] +fun test_is_hello_world() { + let expected = b"Hello, World!".to_string(); + assert!(hello_world() == expected) } diff --git a/packages/samples/sources/your-first-move/hello_world_debug.move b/packages/samples/sources/your-first-move/hello_world_debug.move index 00b85898..99de59e9 100644 --- a/packages/samples/sources/your-first-move/hello_world_debug.move +++ b/packages/samples/sources/your-first-move/hello_world_debug.move @@ -1,21 +1,21 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -module book::hello_world_debug { - use std::string::String; - use std::debug; +module book::hello_world_debug; - public fun hello_world(): String { - let result = b"Hello, World!".to_string(); - debug::print(&result); - result - } +use std::string::String; +use std::debug; - #[test] - fun test_is_hello_world() { - let expected = b"Hello, World!".to_string(); - let actual = hello_world(); +public fun hello_world(): String { + let result = b"Hello, World!".to_string(); + debug::print(&result); + result +} + +#[test] +fun test_is_hello_world() { + let expected = b"Hello, World!".to_string(); + let actual = hello_world(); - assert!(actual == expected, 0) - } + assert!(actual == expected) } diff --git a/packages/samples/sources/your-first-move/hello_world_docs.move b/packages/samples/sources/your-first-move/hello_world_docs.move index d100e7ec..3ad5add2 100644 --- a/packages/samples/sources/your-first-move/hello_world_docs.move +++ b/packages/samples/sources/your-first-move/hello_world_docs.move @@ -2,20 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 /// This module contains a function that returns a string "Hello, World!". -module book::hello_world_docs { - use std::string::String; +module book::hello_world_docs; - /// As the name says: returns a string "Hello, World!". - public fun hello_world(): String { - b"Hello, World!".to_string() - } +use std::string::String; - #[test] - /// This is a test for the `hello_world` function. - fun test_is_hello_world() { - let expected = b"Hello, World!".to_string(); - let actual = hello_world(); +/// As the name says: returns a string "Hello, World!". +public fun hello_world(): String { + b"Hello, World!".to_string() +} + +#[test] +/// This is a test for the `hello_world` function. +fun test_is_hello_world() { + let expected = b"Hello, World!".to_string(); + let actual = hello_world(); - assert!(actual == expected, 0) - } + assert!(actual == expected) } diff --git a/packages/todo_list/sources/todo_list.move b/packages/todo_list/sources/todo_list.move index 787ed95d..abb89722 100644 --- a/packages/todo_list/sources/todo_list.move +++ b/packages/todo_list/sources/todo_list.move @@ -1,43 +1,43 @@ // ANCHOR: all /// Module: todo_list -module todo_list::todo_list { - use std::string::String; - - /// List of todos. Can be managed by the owner and shared with others. - public struct TodoList has key, store { - id: UID, - items: vector - } - - /// Create a new todo list. - public fun new(ctx: &mut TxContext): TodoList { - let list = TodoList { - id: object::new(ctx), - items: vector[] - }; - - (list) - } - - /// Add a new todo item to the list. - public fun add(list: &mut TodoList, item: String) { - list.items.push_back(item); - } - - /// Remove a todo item from the list by index. - public fun remove(list: &mut TodoList, index: u64): String { - list.items.remove(index) - } - - /// Delete the list and the capability to manage it. - public fun delete(list: TodoList) { - let TodoList { id, items: _ } = list; - id.delete(); - } - - /// Get the number of items in the list. - public fun length(list: &TodoList): u64 { - list.items.length() - } +module todo_list::todo_list; + +use std::string::String; + +/// List of todos. Can be managed by the owner and shared with others. +public struct TodoList has key, store { + id: UID, + items: vector +} + +/// Create a new todo list. +public fun new(ctx: &mut TxContext): TodoList { + let list = TodoList { + id: object::new(ctx), + items: vector[] + }; + + (list) +} + +/// Add a new todo item to the list. +public fun add(list: &mut TodoList, item: String) { + list.items.push_back(item); +} + +/// Remove a todo item from the list by index. +public fun remove(list: &mut TodoList, index: u64): String { + list.items.remove(index) +} + +/// Delete the list and the capability to manage it. +public fun delete(list: TodoList) { + let TodoList { id, items: _ } = list; + id.delete(); +} + +/// Get the number of items in the list. +public fun length(list: &TodoList): u64 { + list.items.length() } // ANCHOR_END: all diff --git a/reference/src/SUMMARY.md b/reference/src/SUMMARY.md index 55b12075..e544e8df 100644 --- a/reference/src/SUMMARY.md +++ b/reference/src/SUMMARY.md @@ -18,6 +18,7 @@ - [Local Variables and Scopes](variables.md) - [Equality](equality.md) - [Abort and Assert](abort-and-assert.md) + - [Clever Errors](abort-and-assert/clever-errors.md) - [Control Flow](control-flow.md) - [Conditional Expressions](control-flow/conditionals.md) - [Loops](control-flow/loops.md) diff --git a/reference/src/abort-and-assert/clever-errors.md b/reference/src/abort-and-assert/clever-errors.md new file mode 100644 index 00000000..d8ba8c6e --- /dev/null +++ b/reference/src/abort-and-assert/clever-errors.md @@ -0,0 +1,234 @@ +# Clever Errors + +Clever errors are a feature that allows for more informative error messages when an assertion fails +or an abort is raised. They are a source feature and compile to a `u64` abort code value that +contains the information needed to access the line number, constant name, and constant value given +the clever error code and the module that the clever error constant was declared in. Because of this +compilation, post-processing is required to go from the `u64` abort code value to a human-readable +error message. The post-processing is automatically performed by the Sui GraphQL server, as well as +the Sui CLI. If you want to manually decode a clever abort code, you can use the process outlined in +[Inflating Clever Abort Codes](#inflating-clever-abort-codes) to do so. + +> Clever errors include source line information amongst other data. Because of this their value may +> change due to any changes in the source file (e.g., due to auto-formatting, adding a new module +> member, or adding a newline). + +## Clever Abort Codes + +Clever abort codes allow you to use non-u64 constants as abort codes as long as the constants are +annotated with the `#[error]` attribute. They can be used both in assertions, and as codes to +`abort`. + +```move +module 0x42::a_module; + +#[error] +const EIsThree: vector = b"The value is three"; + +// Will abort with `EIsThree` if `x` is 3 +public fun double_except_three(x: u64): u64 { + assert!(x != 3, EIsThree); + x * x +} + +// Will always abort with `EIsThree` +public fun clever_abort() { + abort EIsThree +} +``` + +In this example, the `EIsThree` constant is a `vector`, which is not a `u64`. However, the +`#[error]` attribute allows the constant to be used as an abort code, and will at runtime produce a +`u64` abort code value that holds: + +1. A set tag-bit that indicates that the abort code is a clever abort code. +2. The line number of where the abort occured in the source file (e.g., 7). +3. The index in the module's identifier table for the constant's name (e.g., `EIsThree`). +4. The index of the constant's value in the module's constant table (e.g., `b"The value is three"`). + +In hex, if `double_except_three(3)` is called, it will abort with a `u64` abort code as follows: + +``` +0x8000_0007_0001_0000 + ^ ^ ^ ^ + | | | | + | | | | + | | | +-- Constant value index = 0 (b"The value is three") + | | +-- Constant name index = 1 (EIsThree) + | +-- Line number = 7 (line of the assertion) + +-- Tag bit = 0b1000_0000_0000_0000 +``` + +And could be rendered as a human-readable error message as (e.g.) + +``` +Error from '0x42::a_module::double_except_three' (line 7), abort 'EIsThree': "The value is three" +``` + +The exact formatting of this message may vary depending on the tooling used to decode the clever +error however all of the information needed to generate a human-readable error message like the +above is present in the `u64` abort code when coupled with the module where the error occurred. + +> Clever abort code values do _not_ need to be a `vector` -- it can be any valid constant type +> in Move. + +## Assertions with no Abort Codes + +Assertions and `abort` statements without an abort code will automatically derive an abort code from +the source line number and will be encoded in the clever error format with the constant name and +constant value information will be filled with sentinel values of `0xffff` each. E.g., + +```move +module 0x42::a_module; + +#[test] +fun assert_false(x: bool) { + assert!(false); +} + +#[test] +fun abort_no_code() { + abort +} +``` + +Both of these will produce a `u64` abort code value that holds: + +1. A set tag-bit that indicates that the abort code is a clever abort code. +2. The line number of where the abort occured in the source file (e.g., 6). +3. A sentinel value of `0xffff` for the index into the module's identifier table for the constant's + name. +4. A sentinel value of `0xffff` for the index of the constant's value in the module's constant + table. + +In hex, if `assert_false(3)` is called, it will abort with a `u64` abort code as follows: + +``` +0x8000_0004_ffff_ffff + ^ ^ ^ ^ + | | | | + | | | | + | | | +-- Constant value index = 0xffff (sentinel value) + | | +-- Constant name index = 0xffff (sentinel value) + | +-- Line number = 4 (linke of the assertion) + +-- Tag bit = 0b1000_0000_0000_0000 +``` + +## Clever Errors and Macros + +The line number information in clever abort codes are derived from the source file at the location +where the abort occurs. In particular, for a function this will be the line number within in the +function, however for macros, this will be the location where the macro is invoked. This can be +quite useful when writing macros as it provides a way for users to use macros that may raise abort +conditions and still get useful error messages. + +```move +module 0x42::macro_exporter; + +public macro fun assert_false() { + assert!(false); +} + +public macro fun abort_always() { + abort +} + +public fun assert_false_fun() { + assert!(false); // Will always abort with the line number of this invocation +} + +public fun abort_always_fun() { + abort // Will always abort with the line number of this invocation +} +``` + +Then in a module that uses these macros: + +```move +module 0x42::user_module; + +use 0x42::macro_exporter::{ + assert_false, + abort_always, + assert_false_fun, + abort_always_fun +}; + +fun invoke_assert_false() { + assert_false!(); // Will abort with the line number of this invocation +} + +fun invoke_abort_always() { + abort_always!(); // Will abort with the line number of this invocation +} + +fun invoke_assert_false_fun() { + assert_false_fun(); // Will abort with the line number of the assertion in `assert_false_fun` +} + +fun invoke_abort_always_fun() { + abort_always_fun(); // Will abort with the line number of the `abort` in `abort_always_fun` +} +``` + +## Inflating Clever Abort Codes + +Precisely, the layout of a clever abort code is as follows: + +``` + +|||||| ++--------+----------+--------------------+-------------------------+-----------------------+ +| 1-bit | 15-bits | 16-bits | 16-bits | 16-bits | + +``` + +Note that the Move abort will come with some additional information -- importantly in our case the +module where the error occurred. This is important because the identifier index, and constant index +are relative to the module's identifier and constant tables (if not set the sentinel values). + +> To decode a clever abort code, you will need to know the module where the error occurred if either +> the identifier index or constant index are not set to the sentinel value of `0xffff`. + +In pseudo-code, you can decode a clever abort code as follows: + +```rust +// Information available in the MoveAbort +let clever_abort_code: u64 = ...; +let (package_id, module_name): (PackageStorageId, ModuleName) = ...; + +let is_clever_abort = (clever_abort_code & 0x8000_0000_0000_0000) != 0; + +if is_clever_abort { + // Get line number, identifier index, and constant index + // Identifier and constant index are sentinel values if set to '0xffff' + let line_number = ((clever_abort_code & 0x0000_ffff_0000_0000) >> 32) as u16; + let identifier_index = ((clever_abort_code & 0x0000_0000_ffff_0000) >> 16) as u16; + let constant_index = ((clever_abort_code & 0x0000_0000_0000_ffff)) as u16; + + // Print the line error message + print!("Error from '{}::{}' (line {})", package_id, module_name, line_number); + + // No need to print anything or load the module if both are sentinel values + if identifier_index == 0xffff && constant_index == 0xffff { + return; + } + + // Only needed if constant name and value are not 0xffff + let module: CompiledModule = fetch_module(package_id, module_name); + + // Print the constant name (if any) + if identifier_index != 0xffff { + let constant_name = module.get_identifier_at_table_index(identifier_index); + print!(", '{}'", constant_name); + } + + // Print the constant value (if any) + if constant_index != 0xffff { + let constant_value = module.get_constant_at_table_index(constant_index).deserialize_on_constant_type().to_string(); + print!(": {}", constant_value); + } + + return; +} +``` diff --git a/reference/src/control-flow/labeled-control-flow.md b/reference/src/control-flow/labeled-control-flow.md index b408086e..311f62f0 100644 --- a/reference/src/control-flow/labeled-control-flow.md +++ b/reference/src/control-flow/labeled-control-flow.md @@ -129,7 +129,7 @@ contexts. To clarify program behavior, you may only use `break` and `continue` with loop labels, while `return` will only work with block labels. To this end, the following programs produce errors: -``` +```move fun bad_loop() { 'name: loop { return 'name 5 diff --git a/reference_zh/src/SUMMARY.md b/reference_zh/src/SUMMARY.md index 14050adc..5abf7133 100644 --- a/reference_zh/src/SUMMARY.md +++ b/reference_zh/src/SUMMARY.md @@ -18,6 +18,7 @@ - [局部变量和作用域](variables.md) - [相等性](equality.md) - [错误处理](abort-and-assert.md) + - [Clever Errors](abort-and-assert/clever-errors.md) - [流程控制](control-flow.md) - [条件表达式](control-flow/conditionals.md) - [循环](control-flow/loops.md) diff --git a/reference_zh/src/abort-and-assert/clever-errors.md b/reference_zh/src/abort-and-assert/clever-errors.md new file mode 100644 index 00000000..3f92f97f --- /dev/null +++ b/reference_zh/src/abort-and-assert/clever-errors.md @@ -0,0 +1,205 @@ +# Clever Errors(智能错误) + +Clever Errors(智能错误)是一种功能,用于在断言(assert)失败或触发 `abort` 时,提供更具信息性的错误消息。它们是源级特性(source feature),在编译后会转换成一个 `u64` 的中断码(abort code),此 `u64` 值中包含了生成可读错误信息所需的行号、常量名称以及常量值的索引。基于该转换的特性,后续需要对这个 `u64` 中断码进行处理,才能得到可读的错误消息。Sui 的 GraphQL 服务器和 Sui CLI 都会自动执行这样的后处理。如果您想手动解析 Clever 错误中断码,可参考[展开(inflate)Clever 中断码](#inflating-clever-abort-codes)的流程进行操作。 + +> Clever Errors 会在错误信息中包含源文件的行号等数据。因此,若源文件发生任何变动(例如自动格式化、添加新的模块成员或者新增空行),Clever Error 的实际数值都可能发生变化。 + +## Clever 中断码 + +Clever 中断码允许您将非 `u64` 类型的常量作为中断码,只需要在常量上加上 `#[error]` 属性即可。它们既可在断言(assert)语句中使用,也可直接作为 `abort` 中断码使用。 + +```move +module 0x42::a_module; + +#[error] +const EIsThree: vector = b"The value is three"; + +// 若 `x` 为 3,则以 `EIsThree` 作为中断码 +public fun double_except_three(x: u64): u64 { + assert!(x != 3, EIsThree); + x * x +} + +// 始终以 `EIsThree` 作为中断码 +public fun clever_abort() { + abort EIsThree +} +``` + +在上述示例中,`EIsThree` 常量的类型为 `vector`,并非 `u64`。然而,由于有 `#[error]` 属性修饰,这个常量就能够用作中断码,并在运行时转换成一个 `u64` 的中断码值,其中包含: + +1. 一个标记位(tag-bit),指示该中断码属于 Clever 中断码。 +2. 触发中断(abort)所在行号(例如行号 7)。 +3. 该常量名字在模块标识符(identifier)表中的索引(如 `EIsThree`)。 +4. 该常量值在模块常量表(constant table)中的索引(如 `b"The value is three"`)。 + +如果调用 `double_except_three(3)`,它会以一个 `u64` 中断码的十六进制值中断,例如: + +``` +0x8000_0007_0001_0000 + ^ ^ ^ ^ + | | | | + | | | | + | | | +-- 常量值索引 = 0 (b"The value is three") + | | +-- 常量名称索引 = 1 (EIsThree) + | +-- 行号 = 7(触发断言的位置) + +-- 标记位 = 0b1000_0000_0000_0000 +``` + +再将其还原成可读的错误消息,示例如下: + +``` +Error from '0x42::a_module::double_except_three' (line 7), abort 'EIsThree': "The value is three" +``` + +具体的消息格式可能因解析工具而异,但在 `u64` 中断码结合该错误所在的模块后,就可以获取到生成类似上述人类可读错误消息所需的全部信息。 + +> Clever 中断码并不要求常量类型必须是 `vector`,任何在 Move 中可用的常量类型都可以搭配 `#[error]` 用作 Clever 中断码。 + +## 无中断码的断言 + +在断言(`assert!`)或 `abort` 语句中如果没有显式的中断码,将默认自动生成基于源码行号的中断码,并采用 Clever Error 的编码方式。此时,其常量名称与常量值索引将使用 `0xffff` 这两个哨兵(sentinel)值。例如: + +```move +module 0x42::a_module; + +#[test] +fun assert_false(x: bool) { + assert!(false); +} + +#[test] +fun abort_no_code() { + abort +} +``` + +这两种情况都会产生一个 `u64` 中断码,其中包括: + +1. 一个标记位(tag-bit),表明该中断码是 Clever 中断码。 +2. 触发断言或 `abort` 的源码行号(例如第 6 行)。 +3. 模块标识符表索引的哨兵值 `0xffff`。 +4. 模块常量表索引的哨兵值 `0xffff`。 + +若调用 `assert_false(3)`,它会触发一个 `u64` 中断码,可能如下所示: + +``` +0x8000_0004_ffff_ffff + ^ ^ ^ ^ + | | | | + | | | | + | | | +-- 常量值索引 = 0xffff(哨兵值) + | | +-- 常量名称索引 = 0xffff(哨兵值) + | +-- 行号 = 4(触发断言的位置) + +-- 标记位 = 0b1000_0000_0000_0000 +``` + +## Clever Errors 与宏(Macros) + +Clever 中断码中的行号信息来源于中断发生的源码位置。对于一般函数来说,这通常是触发断言或 `abort` 代码行在函数体内的行号;但对于宏(macro),则使用宏调用处的行号。这在编写宏时非常有用,因为这样用户在使用宏时,仍然能获得准确且有用的错误消息。 + +```move +module 0x42::macro_exporter; + +public macro fun assert_false() { + assert!(false); +} + +public macro fun abort_always() { + abort +} + +public fun assert_false_fun() { + assert!(false); // 一直中断,并使用此处的行号 +} + +public fun abort_always_fun() { + abort // 一直中断,并使用此处的行号 +} +``` + +随后在另一个模块中使用这些宏: + +```move +module 0x42::user_module; + +use 0x42::macro_exporter::{ + assert_false, + abort_always, + assert_false_fun, + abort_always_fun +}; + +fun invoke_assert_false() { + assert_false!(); // 中断时使用当前这行的行号 +} + +fun invoke_abort_always() { + abort_always!(); // 中断时使用当前这行的行号 +} + +fun invoke_assert_false_fun() { + assert_false_fun(); // 中断时,会使用 assert_false_fun 中断言那行的行号 +} + +fun invoke_abort_always_fun() { + abort_always_fun(); // 中断时,会使用 abort_always_fun 中 `abort` 行的行号 +} +``` + +## 展开(Inflating)Clever 中断码 + +具体而言,Clever 中断码的布局如下: + +``` +|||||| ++--------+----------+--------------------+-------------------------+-----------------------+ +| 1-bit | 15-bits | 16-bits | 16-bits | 16-bits | +``` + +值得注意的是,Move 中断(MoveAbort)还会附带一些额外信息——对我们来说,最重要的是触发错误的模块信息。因为常量名称和常量值的索引要在该模块的标识符表和常量表中才能解析(如果它们不是哨兵值的话)。 + +> 若要解析一个 Clever 中断码,您需要知道该错误发生在哪个模块内(即需知道对应的 `package_id` 和 `module_name`),尤其当常量名称或常量值索引并非 `0xffff` 的情况下。 + +以下示例的伪代码展示了如何解析一个 Clever 中断码: + +```rust +// MoveAbort 提供的一些信息 +let clever_abort_code: u64 = ...; +let (package_id, module_name): (PackageStorageId, ModuleName) = ...; + +let is_clever_abort = (clever_abort_code & 0x8000_0000_0000_0000) != 0; + +if is_clever_abort { + // 获取行号、标识符索引和常量索引 + // 若索引为 0xffff,表示该字段为哨兵值 + let line_number = ((clever_abort_code & 0x0000_ffff_0000_0000) >> 32) as u16; + let identifier_index = ((clever_abort_code & 0x0000_0000_ffff_0000) >> 16) as u16; + let constant_index = ((clever_abort_code & 0x0000_0000_0000_ffff)) as u16; + + // 打印行号错误消息 + print!("Error from '{}::{}' (line {})", package_id, module_name, line_number); + + // 若标识符与常量索引都为哨兵值,则无需再打印或加载模块信息 + if identifier_index == 0xffff && constant_index == 0xffff { + return; + } + + // 如果常量名称和常量值的索引都非 0xffff,则需加载模块 + let module: CompiledModule = fetch_module(package_id, module_name); + + // 打印常量名称(如果存在) + if identifier_index != 0xffff { + let constant_name = module.get_identifier_at_table_index(identifier_index); + print!(", '{}'", constant_name); + } + + // 打印常量值(如果存在) + if constant_index != 0xffff { + let constant_value = module.get_constant_at_table_index(constant_index).deserialize_on_constant_type().to_string(); + print!(": {}", constant_value); + } + + return; +} +``` diff --git a/theme/highlight.js b/theme/highlight.js index 0c23b73a..a63c3615 100644 --- a/theme/highlight.js +++ b/theme/highlight.js @@ -441,7 +441,7 @@ hljs.registerLanguage('move', function(hljs) { // module definition scope: 'module', begin: /\bmodule\b/, - end: /\{/, + end: /[;{]/, keywords: 'module', contains: [ BLOCK_COMMENT,