diff --git a/README.md b/README.md index 460261c1..cd669790 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ # The Move Book -This is the repository for [the Move Book](https://move-book.com). +This is the repository for [the Move Book](https://move-book.com) and [Move Language Reference](https://move-book.com/reference). + +## Structure + +- Two books are placed in the `book` and `reference` directories. The `book` directory contains the main book, and the `reference` directory contains the reference book. + +- The `theme` directory is linked to both books and contains the theme files, fonts and styles. + +- The `packages` directory contains the code samples used in both books. ## Archive diff --git a/.bak/todo-list.md b/book/.bak/todo-list.md similarity index 100% rename from .bak/todo-list.md rename to book/.bak/todo-list.md diff --git a/.bak/your-first-move.md b/book/.bak/your-first-move.md similarity index 100% rename from .bak/your-first-move.md rename to book/.bak/your-first-move.md diff --git a/book.toml b/book/book.toml similarity index 95% rename from book.toml rename to book/book.toml index 013d83ef..58cf424c 100644 --- a/book.toml +++ b/book/book.toml @@ -4,12 +4,12 @@ description = "First book about the Move programming language and the Move VM. M authors = ["Damir Shamanaev"] language = "en" multilingual = false -src = "book" -repository = "damirka/move-book" +src = "src" +repository = "MystenLabs/move-book" [build] build-dir = "build" -extra-watch-dirs = ["packages"] # rebuild on Move Source changes +extra-watch-dirs = ["../packages"] # rebuild on Move Source changes [output.html] additional-css = ["theme/css/custom.css"] diff --git a/book/SUMMARY.md b/book/src/SUMMARY.md similarity index 100% rename from book/SUMMARY.md rename to book/src/SUMMARY.md diff --git a/book/appendix/acknowledgements.md b/book/src/appendix/acknowledgements.md similarity index 100% rename from book/appendix/acknowledgements.md rename to book/src/appendix/acknowledgements.md diff --git a/book/appendix/contributing.md b/book/src/appendix/contributing.md similarity index 100% rename from book/appendix/contributing.md rename to book/src/appendix/contributing.md diff --git a/book/appendix/glossary.md b/book/src/appendix/glossary.md similarity index 100% rename from book/appendix/glossary.md rename to book/src/appendix/glossary.md diff --git a/book/appendix/publications.md b/book/src/appendix/publications.md similarity index 100% rename from book/appendix/publications.md rename to book/src/appendix/publications.md diff --git a/book/appendix/references.md b/book/src/appendix/references.md similarity index 100% rename from book/appendix/references.md rename to book/src/appendix/references.md diff --git a/book/basic-syntax/README.md b/book/src/basic-syntax/README.md similarity index 100% rename from book/basic-syntax/README.md rename to book/src/basic-syntax/README.md diff --git a/book/basic-syntax/address.md b/book/src/basic-syntax/address.md similarity index 80% rename from book/basic-syntax/address.md rename to book/src/basic-syntax/address.md index 3b28364a..63a140c2 100644 --- a/book/basic-syntax/address.md +++ b/book/src/basic-syntax/address.md @@ -21,7 +21,7 @@ Links: To represent [addresses](./../concepts/address.md), Move uses a special type called `address`. It is a 32 byte value that can be used to represent any address on the blockchain. Addresses are used in two syntax forms: hexadecimal addresses prefixed with `0x` and named addresses. ```move -{{#include ../../packages/samples/sources/basic-syntax/address.move:address_literal}} +{{#include ../../../packages/samples/sources/basic-syntax/address.move:address_literal}} ``` An address literal starts with the `@` symbol followed by a hexadecimal number or an identifier. The hexadecimal number is interpreted as a 32 byte value. The identifier is looked up in the [Move.toml](./../concepts/manifest.md) file and replaced with the corresponding address by the compiler. If the identifier is not found in the Move.toml file, the compiler will throw an error. @@ -32,15 +32,15 @@ Sui Framework offers a set of helper functions to work with addresses. Given tha Example: Convert an address to a `u256` type and back. ```move -{{#include ../../packages/samples/sources/basic-syntax/address.move:to_u256}} +{{#include ../../../packages/samples/sources/basic-syntax/address.move:to_u256}} ``` Example: Convert an address to a `vector` type and back. ```move -{{#include ../../packages/samples/sources/basic-syntax/address.move:to_bytes}} +{{#include ../../../packages/samples/sources/basic-syntax/address.move:to_bytes}} ``` Example: Convert an address into a string. ```move -{{#include ../../packages/samples/sources/basic-syntax/address.move:to_string}} +{{#include ../../../packages/samples/sources/basic-syntax/address.move:to_string}} ``` diff --git a/book/basic-syntax/assert-and-abort.md b/book/src/basic-syntax/assert-and-abort.md similarity index 89% rename from book/basic-syntax/assert-and-abort.md rename to book/src/basic-syntax/assert-and-abort.md index 5ce6d93d..187356c4 100644 --- a/book/basic-syntax/assert-and-abort.md +++ b/book/src/basic-syntax/assert-and-abort.md @@ -29,7 +29,7 @@ A transaction can either succeed or fail. Successful execution applies all the c The `abort` keyword is used to abort the execution of a transaction. It is used in combination with an abort code, which will be returned to the caller of the transaction. The abort code is an integer of type `u64` and can be any value. ```move -{{#include ../../packages/samples/sources/basic-syntax/assert-and-abort.move:abort}} +{{#include ../../../packages/samples/sources/basic-syntax/assert-and-abort.move:abort}} ``` The code above will, of course, abort with abort code `1`. @@ -39,7 +39,7 @@ 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`. ```move -{{#include ../../packages/samples/sources/basic-syntax/assert-and-abort.move:assert}} +{{#include ../../../packages/samples/sources/basic-syntax/assert-and-abort.move:assert}} ``` ## Error constants @@ -47,7 +47,7 @@ The `assert!` macro is a built-in macro that can be used to assert a condition. To make error codes more descriptive, it is a good practice to define error constants. Error constants are defined as `const` declarations and are usually prefixed with `E` followed by a camel case name. Error constants are no different from other constants and don't have special handling. So their addition is purely a practice for better code readability. ```move -{{#include ../../packages/samples/sources/basic-syntax/assert-and-abort.move:error_const}} +{{#include ../../../packages/samples/sources/basic-syntax/assert-and-abort.move:error_const}} ``` ## Further reading diff --git a/book/basic-syntax/comments.md b/book/src/basic-syntax/comments.md similarity index 84% rename from book/basic-syntax/comments.md rename to book/src/basic-syntax/comments.md index b9ab3707..9a0c692b 100644 --- a/book/basic-syntax/comments.md +++ b/book/src/basic-syntax/comments.md @@ -17,13 +17,13 @@ Comments are a way to add notes or document your code. They are ignored by the c ## Line comment ```Move -{{#include ../../packages/samples/sources/basic-syntax/comments.move:line}} +{{#include ../../../packages/samples/sources/basic-syntax/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/basic-syntax/comments.move:line_2}} +{{#include ../../../packages/samples/sources/basic-syntax/comments.move:line_2}} ``` ## Block comment @@ -31,7 +31,7 @@ You can use double slash `//` to comment out the rest of the line. Everything af Block comments are used to comment out a block of code. They start with `/*` and end with `*/`. Everything between `/*` and `*/` will be ignored by the compiler. You can use block comments to 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/basic-syntax/comments.move:block}} +{{#include ../../../packages/samples/sources/basic-syntax/comments.move:block}} ``` This example is a bit extreme, but it shows how you can use block comments to comment out a part of a line. @@ -41,7 +41,7 @@ This example is a bit extreme, but it shows how you can use block comments to co Documentation comments are special comments that are used to generate documentation for your code. They are similar to block comments, but they start with three slashes `///` and are placed before the definition of the item they document. ```Move -{{#include ../../packages/samples/sources/basic-syntax/comments.move:doc}} +{{#include ../../../packages/samples/sources/basic-syntax/comments.move:doc}} ``` diff --git a/book/basic-syntax/constants.md b/book/src/basic-syntax/constants.md similarity index 89% rename from book/basic-syntax/constants.md rename to book/src/basic-syntax/constants.md index aff66343..eb0d03ba 100644 --- a/book/basic-syntax/constants.md +++ b/book/src/basic-syntax/constants.md @@ -21,7 +21,7 @@ Links: Constants are immutable values that are defined at the module level. They often serve as a way to give names to static values that are used throughout a module. For example, if there's a default price for a product, you might define a constant for it. Constants are stored in the module's bytecode, and each time they are used, the value is copied. ```move -{{#include ../../packages/samples/sources/basic-syntax/constants.move:shop_price}} +{{#include ../../../packages/samples/sources/basic-syntax/constants.move:shop_price}} ``` ## Naming Convention @@ -29,7 +29,7 @@ Constants are immutable values that are defined at the module level. They often Constants must start with a capital letter - this is enforced at the compiler level. For constants used as a value, there's a convention to use uppercase letters and underscores to separate words. It's a way to make constants stand out from other identifiers in the code. One exception is made for [error constants](./assert-and-abort.md#assert-and-abort), which are written in ECamelCase. ```move -{{#include ../../packages/samples/sources/basic-syntax/constants.move:naming}} +{{#include ../../../packages/samples/sources/basic-syntax/constants.move:naming}} ``` ## Constants are Immutable @@ -52,7 +52,7 @@ module book::immutable_constants { A common use case for an application is to define a set of constants that are used throughout the codebase. But due to constants being private to the module, they can't be accessed from other modules. One way to solve this is to define a "config" module that exports the constants. ```move -{{#include ../../packages/samples/sources/basic-syntax/constants.move:config}} +{{#include ../../../packages/samples/sources/basic-syntax/constants.move:config}} ``` This way other modules can import and read the constants, and the update process is simplified. If the constants need to be changed, only the config module needs to be updated during the package upgrade. diff --git a/book/basic-syntax/control-flow.md b/book/src/basic-syntax/control-flow.md similarity index 90% rename from book/basic-syntax/control-flow.md rename to book/src/basic-syntax/control-flow.md index a966e4e0..f2802927 100644 --- a/book/basic-syntax/control-flow.md +++ b/book/src/basic-syntax/control-flow.md @@ -40,13 +40,13 @@ if () else ; Just like any other expression, `if` requires a semicolon, if there are other expressions following it. The `else` keyword is optional, except for the case when the resulting value is assigned to a variable. We will cover this below. ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:if_condition}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:if_condition}} ``` Let's see how we can use `if` and `else` to assign a value to a variable: ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:if_else}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:if_else}} ``` Here we assign the value of the `if` expression to the variable `y`. If `x` is greater than 0, `y` will be assigned the value 1, otherwise 0. The `else` block is necessary, because both branches must return a value of the same type. If we omit the `else` block, the compiler will throw an error. @@ -74,7 +74,7 @@ while () { ; }; Here is an example of a `while` loop with a very simple condition: ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:while_loop}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:while_loop}} ``` ## Infinite `loop` @@ -82,7 +82,7 @@ Here is an example of a `while` loop with a very simple condition: Now let's imagine a scenario where the boolean expression is always `true`. For example, if we literally passed `true` to the `while` condition. As you might expect, this would create an infinite loop, and this is almost what the `loop` statement works like. ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:infinite_while}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:infinite_while}} ``` An infinite `while`, or `while` without a condition, is a `loop`. The syntax for it is simple: @@ -94,7 +94,7 @@ loop { ; }; Let's rewrite the previous example using `loop` instead of `while`: ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:infinite_loop}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:infinite_loop}} ``` @@ -114,7 +114,7 @@ break The `break` statement is used to stop the execution of a loop and exit it early. It is often used in combination with a conditional statement to exit the loop when a certain condition is met. To illustrate this point, let's turn the infinite `loop` from the previous example into something that looks and behaves more like a `while` loop: ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:break_loop}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:break_loop}} ``` Almost identical to the `while` loop, right? The `break` statement is used to exit the loop when `x` is 5. If we remove the `break` statement, the loop will run forever, just like the previous example. @@ -131,7 +131,7 @@ continue The example below skips odd numbers and prints only even numbers from 0 to 10: ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:continue_loop}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:continue_loop}} ``` `break` and `continue` statements can be used in both `while` and `loop` loops. @@ -147,7 +147,7 @@ return Here is an example of a function that returns a value when a certain condition is met: ```move -{{#include ../../packages/samples/sources/basic-syntax/control-flow.move:return_statement}} +{{#include ../../../packages/samples/sources/basic-syntax/control-flow.move:return_statement}} ``` Unlike in other languages, the `return` statement is not required for the last expression in a function. The last expression in a function block is automatically returned. However, the `return` statement is useful when we want to exit a function early if a certain condition is met. diff --git a/book/basic-syntax/copy-ability.md b/book/src/basic-syntax/copy-ability.md similarity index 85% rename from book/basic-syntax/copy-ability.md rename to book/src/basic-syntax/copy-ability.md index 45069f21..b85f6129 100644 --- a/book/basic-syntax/copy-ability.md +++ b/book/src/basic-syntax/copy-ability.md @@ -5,13 +5,13 @@ In Move, the *copy* ability on a type indicates that the instance or the value o However, Move type system allows you to define custom types with the *copy* ability. ```move -{{#include ../../packages/samples/sources/basic-syntax/copy-ability.move:copyable}} +{{#include ../../../packages/samples/sources/basic-syntax/copy-ability.move:copyable}} ``` In the example above, we define a custom type `Copyable` with the *copy* ability. This means that instances of `Copyable` can be copied, both implicitly and explicitly. ```move -{{#include ../../packages/samples/sources/basic-syntax/copy-ability.move:copyable_test}} +{{#include ../../../packages/samples/sources/basic-syntax/copy-ability.move:copyable_test}} ``` In the example above, `a` is copied to `b` implicitly, and then explicitly copied to `c` using the dereference operator. If `Copyable` did not have the *copy* ability, the code would not compile, and the Move compiler would raise an error. @@ -21,7 +21,7 @@ In the example above, `a` is copied to `b` implicitly, and then explicitly copie The `copy` ability is closely related to [`drop` ability](./drop-ability.md). If a type has the *copy* ability, very likely that it should have `drop` too. This is because the *drop* ability is required to clean up the resources when the instance is no longer needed. If a type has only *copy*, then managing its instances gets more complicated, as the values cannot be ignored. ```move -{{#include ../../packages/samples/sources/basic-syntax/copy-ability.move:copy_drop}} +{{#include ../../../packages/samples/sources/basic-syntax/copy-ability.move:copy_drop}} ``` All of the primitive types in Move behave as if they have the *copy* and *drop* abilities. This means that they can be copied and dropped, and the Move compiler will handle the memory management for them. diff --git a/book/basic-syntax/drop-ability.md b/book/src/basic-syntax/drop-ability.md similarity index 97% rename from book/basic-syntax/drop-ability.md rename to book/src/basic-syntax/drop-ability.md index 04ddc308..1b93c2d3 100644 --- a/book/basic-syntax/drop-ability.md +++ b/book/src/basic-syntax/drop-ability.md @@ -50,7 +50,7 @@ A struct without abilities cannot be discarded, or copied, or stored in the stor The `drop` ability - the simplest of them - allows the instance of a struct to be *ignored* or *discarded*. In many programming languages this behavior is considered default. However, in Move, a struct without the `drop` ability is not allowed to be ignored. This is a safety feature of the Move language, which ensures that all assets are properly handled. An attempt to ignore a struct without the `drop` ability will result in a compilation error. ```move -{{#include ../../packages/samples/sources/basic-syntax/drop-ability.move:main}} +{{#include ../../../packages/samples/sources/basic-syntax/drop-ability.move:main}} ``` The `drop` ability is often used on custom collection types to eliminate the need for special handling of the collection when it is no longer needed. For example, a `vector` type has the `drop` ability, which allows the vector to be ignored when it is no longer needed. However, the biggest feature of Move's type system is the ability to not have `drop`. This ensures that the assets are properly handled and not ignored. diff --git a/book/basic-syntax/expression.md b/book/src/basic-syntax/expression.md similarity index 83% rename from book/basic-syntax/expression.md rename to book/src/basic-syntax/expression.md index 441952bf..fc38e3bd 100644 --- a/book/basic-syntax/expression.md +++ b/book/src/basic-syntax/expression.md @@ -15,7 +15,7 @@ In the [Primitive Types](./primitive-types.md) section, we introduced the basic - `x"0A"` HEX literal for byte values ```move -{{#include ../../packages/samples/sources/basic-syntax/expression.move:literals}} +{{#include ../../../packages/samples/sources/basic-syntax/expression.move:literals}} ``` ## Operators @@ -23,7 +23,7 @@ In the [Primitive Types](./primitive-types.md) section, we introduced the basic Ariphmetic, logical, and bitwise operators are used to perform operations on values. The result of an operation is a value, so operators are also expressions. ```move -{{#include ../../packages/samples/sources/basic-syntax/expression.move:operators}} +{{#include ../../../packages/samples/sources/basic-syntax/expression.move:operators}} ``` ## Blocks @@ -31,7 +31,7 @@ Ariphmetic, logical, and bitwise operators are used to perform operations on val A block is a sequence of statements and expressions, and it returns the value of the last expression in the block. A block is written as a pair of curly braces `{}`. A block is an expression, so it can be used anywhere an expression is expected. ```move -{{#include ../../packages/samples/sources/basic-syntax/expression.move:block}} +{{#include ../../../packages/samples/sources/basic-syntax/expression.move:block}} ``` ## Function Calls @@ -39,7 +39,7 @@ A block is a sequence of statements and expressions, and it returns the value of We go into detail about functions in the [Functions](./functions.md) section. However, we already used function calls in the previous sections, so it's worth mentioning them here. A function call is an expression that calls a function and returns the value of the last expression in the function body. ```move -{{#include ../../packages/samples/sources/basic-syntax/expression.move:fun_call}} +{{#include ../../../packages/samples/sources/basic-syntax/expression.move:fun_call}} ``` ## Control Flow Expressions @@ -47,5 +47,5 @@ We go into detail about functions in the [Functions](./functions.md) section. Ho Control flow expressions are used to control the flow of the program. They are also expressions, so they return a value. We cover control flow expressions in the [Control Flow](./control-flow.md) section. Here's a very brief overview: ```move -{{#include ../../packages/samples/sources/basic-syntax/expression.move:control_flow}} +{{#include ../../../packages/samples/sources/basic-syntax/expression.move:control_flow}} ``` diff --git a/book/basic-syntax/function.md b/book/src/basic-syntax/function.md similarity index 79% rename from book/basic-syntax/function.md rename to book/src/basic-syntax/function.md index 2a453bb2..dda16602 100644 --- a/book/basic-syntax/function.md +++ b/book/src/basic-syntax/function.md @@ -3,7 +3,7 @@ Functions are the building blocks of Move programs. They are called from [user transactions](../concepts/user-interaction.md) and from other functions and group executable code into reusable units. Functions can take arguments and return a value. They are declared with the `fun` keyword at the module level. Just like any other module member, by default they're private and can only be accessed from within the module. ```move -{{#include ../../packages/samples/sources/basic-syntax/function.move:math}} +{{#include ../../../packages/samples/sources/basic-syntax/function.move:math}} ``` In this example, we define a function `add` that takes two arguments of type `u64` and returns their sum. The function is called from the `test_add` function, which is a test function located in the same module. In the test we compare the result of the `add` function with the expected value and abort the execution if the result is different. @@ -15,7 +15,7 @@ In this example, we define a function `add` that takes two arguments of type `u6 A function is declared with the `fun` keyword followed by the function name (a valid Move identifier), a list of arguments in parentheses, and a return type. The function body is a block of code that contains a sequence of statements and expressions. The last expression in the function body is the return value of the function. ```move -{{#include ../../packages/samples/sources/basic-syntax/function.move:return_nothing}} +{{#include ../../../packages/samples/sources/basic-syntax/function.move:return_nothing}} ``` ## Accessing functions @@ -23,7 +23,7 @@ A function is declared with the `fun` keyword followed by the function name (a v Just like any other module member, functions can be imported and accessed via a path. The path consists of the module path and the function name separated by `::`. For example, if you have a function called `add` in the `math` module in the `book` package, the path to it will be `book::math::add`, or, if the module is imported, `math::add`. ```move -{{#include ../../packages/samples/sources/basic-syntax/function.move:use_math}} +{{#include ../../../packages/samples/sources/basic-syntax/function.move:use_math}} ``` ## Multiple return values @@ -31,23 +31,23 @@ Just like any other module member, functions can be imported and accessed via a Move functions can return multiple values, which is useful when you need to return more than one value from a function. The return type of the function is a tuple of types. The return value is a tuple of expressions. ```move -{{#include ../../packages/samples/sources/basic-syntax/function.move:tuple_return}} +{{#include ../../../packages/samples/sources/basic-syntax/function.move:tuple_return}} ``` Result of a function call with tuple return has to be unpacked into variables via `let (tuple)` syntax: ```move -{{#include ../../packages/samples/sources/basic-syntax/function.move:tuple_return_imm}} +{{#include ../../../packages/samples/sources/basic-syntax/function.move:tuple_return_imm}} ``` If any of the declared values need to be declared as mutable, the `mut` keyword is placed before the variable name: ```move -{{#include ../../packages/samples/sources/basic-syntax/function.move:tuple_return_mut}} +{{#include ../../../packages/samples/sources/basic-syntax/function.move:tuple_return_mut}} ``` If some of the arguments are not used, they can be ignored with the `_` symbol: ```move -{{#include ../../packages/samples/sources/basic-syntax/function.move:tuple_return_ignore}} +{{#include ../../../packages/samples/sources/basic-syntax/function.move:tuple_return_ignore}} ``` diff --git a/book/basic-syntax/generics.md b/book/src/basic-syntax/generics.md similarity index 83% rename from book/basic-syntax/generics.md rename to book/src/basic-syntax/generics.md index f44aa111..b70dbf07 100644 --- a/book/basic-syntax/generics.md +++ b/book/src/basic-syntax/generics.md @@ -12,13 +12,13 @@ In this chapter we already mentioned the [vector](./vector.md) type, which is a To define a generic type or function, a type signature needs to have a list of generic parameters enclosed in angle brackets (`<` and `>`). The generic parameters are separated by commas. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:container}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:container}} ``` In the example above, `Container` is a generic type with a single type parameter `T`, the `value` field of the container stores the `T`. The `new` function is a generic function with a single type parameter `T`, and it returns a `Container` with the given value. Generic types must be initialed with a concrete type, and generic functions must be called with a concrete type. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:test_container}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:test_container}} ``` In the test function `test_generic` we demonstrate three equivalent ways to create a new `Container` with a `u8` value. Because numeric types need to be inferred, we specify the type of the number literal. @@ -28,19 +28,19 @@ In the test function `test_generic` we demonstrate three equivalent ways to crea You can define a type or function with multiple type parameters. The type parameters are then separated by commas. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:pair}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:pair}} ``` In the example above, `Pair` is a generic type with two type parameters `T` and `U`, and the `new_pair` function is a generic function with two type parameters `T` and `U`. The function returns a `Pair` with the given values. The order of the type parameters is important, and it should match the order of the type parameters in the type signature. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:test_pair}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:test_pair}} ``` If we added another instance where we swapped type parameters in the `new_pair` function, and tried to compare two types, we'd see that the type signatures are different, and cannot be compared. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:test_pair_swap}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:test_pair_swap}} ``` Types for variables `pair1` and `pair2` are different, and the comparison will not compile. @@ -50,13 +50,13 @@ Types for variables `pair1` and `pair2` are different, and the comparison will n In the examples above we focused on instantiating generic types and calling generic functions to create instances of these types. However, the real power of generics is the ability to define shared behavior for the base, generic type, and then use it independently of the concrete types. This is especially useful when working with collections, abstract implementations, and other advanced features in Move. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:user}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:user}} ``` In the example above, `User` is a generic type with a single type parameter `T`, with shared fields `name` and `age`, and the generic `metadata` field which can store any type. No matter what the `metadata` is, all of the instances of `User` will have the same fields and methods. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:update_user}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:update_user}} ``` ## Phantom Type Parameters @@ -64,13 +64,13 @@ In the example above, `User` is a generic type with a single type parameter `T`, In some cases, you may want to define a generic type with a type parameter that is not used in the fields or methods of the type. This is called a *phantom type parameter*. Phantom type parameters are useful when you want to define a type that can hold any other type, but you want to enforce some constraints on the type parameter. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:phantom}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:phantom}} ``` The `Coin` type here does not contain any fields or methods that use the type parameter `T`. It is used to differentiate between different types of coins, and to enforce some constraints on the type parameter `T`. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:test_phantom}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:test_phantom}} ``` In the example above, we demonstrate how to create two different instances of `Coin` with different phantom type parameters `USD` and `EUR`. The type parameter `T` is not used in the fields or methods of the `Coin` type, but it is used to differentiate between different types of coins. It will make sure that the `USD` and `EUR` coins are not mixed up. @@ -80,7 +80,7 @@ In the example above, we demonstrate how to create two different instances of `C Type parameters can be constrained to have certain abilities. This is useful when you need the inner type to allow certain behavior, such as *copy* or *drop*. The syntax for constraining a type parameter is `T: + `. ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:constraints}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:constraints}} ``` Move Compiler will enforce that the type parameter `T` has the specified abilities. If the type parameter does not have the specified abilities, the code will not compile. @@ -88,7 +88,7 @@ Move Compiler will enforce that the type parameter `T` has the specified abiliti ```move -{{#include ../../packages/samples/sources/basic-syntax/generics.move:test_constraints}} +{{#include ../../../packages/samples/sources/basic-syntax/generics.move:test_constraints}} ``` ## Further Reading diff --git a/book/basic-syntax/importing-modules.md b/book/src/basic-syntax/importing-modules.md similarity index 86% rename from book/basic-syntax/importing-modules.md rename to book/src/basic-syntax/importing-modules.md index a27888a2..789fcd2f 100644 --- a/book/basic-syntax/importing-modules.md +++ b/book/src/basic-syntax/importing-modules.md @@ -27,13 +27,13 @@ Modules defined in the same package can import each other. The `use` keyword is ```move // File: sources/module_one.move -{{#include ../../packages/samples/sources/basic-syntax/importing-modules.move:module_one}} +{{#include ../../../packages/samples/sources/basic-syntax/importing-modules.move:module_one}} ``` File: sources/module_two.move ```move // File: sources/module_two.move -{{#include ../../packages/samples/sources/basic-syntax/importing-modules.move:module_two}} +{{#include ../../../packages/samples/sources/basic-syntax/importing-modules.move:module_two}} ``` ## Importing Members @@ -41,7 +41,7 @@ File: sources/module_two.move You can also import specific members from a module. This is useful when you only need a single function or a single type from a module. The syntax is the same as for importing a module, but you add the member name after the module path. ```move -{{#include ../../packages/samples/sources/basic-syntax/importing-modules.move:members}} +{{#include ../../../packages/samples/sources/basic-syntax/importing-modules.move:members}} ``` ## Grouping Imports @@ -49,7 +49,7 @@ You can also import specific members from a module. This is useful when you only Imports can be grouped into a single `use` statement using the curly braces `{}`. This is useful when you need to import multiple members from the same module. Move allows grouping imports from the same module and from the same package. ```move -{{#include ../../packages/samples/sources/basic-syntax/importing-modules.move:grouped}} +{{#include ../../../packages/samples/sources/basic-syntax/importing-modules.move:grouped}} ``` Single function imports are less common in Move, since the function names can overlap and cause confusion. A recommended practice is to import the entire module and use the module path to access the function. Types have unique names and should be imported individually. @@ -57,7 +57,7 @@ Single function imports are less common in Move, since the function names can ov To import members and the module itself in the group import, you can use the `Self` keyword. The `Self` keyword refers to the module itself and can be used to import the module and its members. ```move -{{#include ../../packages/samples/sources/basic-syntax/importing-modules.move:self}} +{{#include ../../../packages/samples/sources/basic-syntax/importing-modules.move:self}} ``` ## Resolving Name Conflicts @@ -65,7 +65,7 @@ To import members and the module itself in the group import, you can use the `Se When importing multiple members from different modules, it is possible to have name conflicts. For example, if you import two modules that both have a function with the same name, you will need to use the module path to access the function. It is also possible to have modules with the same name in different packages. To resolve the conflict and avoid ambiguity, Move offers the `as` keyword to rename the imported member. ```move -{{#include ../../packages/samples/sources/basic-syntax/importing-modules.move:conflict}} +{{#include ../../../packages/samples/sources/basic-syntax/importing-modules.move:conflict}} ``` ## Adding an External Dependency @@ -89,5 +89,5 @@ Normally, packages define their addresses in the `[addresses]` section, so you c To import a module from another package, you use the `use` keyword followed by the module path. The module path consists of the package address (or alias) and the module name separated by `::`. ```move -{{#include ../../packages/samples/sources/basic-syntax/importing-modules.move:external}} +{{#include ../../../packages/samples/sources/basic-syntax/importing-modules.move:external}} ``` diff --git a/book/basic-syntax/module.md b/book/src/basic-syntax/module.md similarity index 88% rename from book/basic-syntax/module.md rename to book/src/basic-syntax/module.md index 4f4320b5..533b9616 100644 --- a/book/basic-syntax/module.md +++ b/book/src/basic-syntax/module.md @@ -21,7 +21,7 @@ Modules are declared using the `module` keyword followed by the package address, 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. ```Move -{{#include ../../packages/samples/sources/basic-syntax/module.move:module}} +{{#include ../../../packages/samples/sources/basic-syntax/module.move:module}} ``` Structs, functions and constants, imports and friend declarations are all part of the module: @@ -38,7 +38,7 @@ Structs, functions and constants, imports and friend declarations are all part o Module address can be specified as both: an address *literal* (does not require the `@` prefix) or a named address specified in the [Package Manifest](../concepts/package-manifest.md). In the example below, both are identical because there's a `book = "0x0"` record in the `[addresses]` section of the `Move.toml`. ```Move -{{#include ../../packages/samples/sources/basic-syntax/module.move:address_literal}} +{{#include ../../../packages/samples/sources/basic-syntax/module.move:address_literal}} ``` Addresses section in the Move.toml: @@ -54,7 +54,7 @@ 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/basic-syntax/module.move:members}} +{{#include ../../../packages/samples/sources/basic-syntax/module.move:members}} ``` ## Address block @@ -64,7 +64,7 @@ Before the introduction of the `address::module_name` syntax, modules were organ > Module addresses can be omitted if modules are organized into `address {}` blocks. ```Move -{{#include ../../packages/samples/sources/basic-syntax/module.move:address_block}} +{{#include ../../../packages/samples/sources/basic-syntax/module.move:address_block}} ``` The modules defined in this code sample will be accessible as: diff --git a/book/basic-syntax/option.md b/book/src/basic-syntax/option.md similarity index 93% rename from book/basic-syntax/option.md rename to book/src/basic-syntax/option.md index 4f28af24..747c98d2 100644 --- a/book/basic-syntax/option.md +++ b/book/src/basic-syntax/option.md @@ -21,7 +21,7 @@ Option type has two variants: `Some` and `None`. `Some` variant contains a value To showcase why Option type is necessary, let's look at an example. Consider an application which takes a user input and stores it in a variable. Some fields are required, and some are optional. For example, a user's middle name is optional. While we could use an empty string to represent the absence of a middle name, it would require extra checks to differentiate between an empty string and a missing middle name. Instead, we can use the `Option` type to represent the middle name. ```move -{{#include ../../packages/samples/sources/basic-syntax/option.move:registry}} +{{#include ../../../packages/samples/sources/basic-syntax/option.move:registry}} ``` In the example above, the `middle_name` field is of type `Option`. This means that the `middle_name` field can either contain a `String` value or be empty. This makes it clear that the middle name is optional, and it avoids the need for extra checks to differentiate between an empty string and a missing middle name. @@ -31,5 +31,5 @@ In the example above, the `middle_name` field is of type `Option`. This To use the `Option` type, you need to import the `std::option` module and use the `Option` type. You can then create an `Option` value using the `some` or `none` methods. ```move -{{#include ../../packages/samples/sources/basic-syntax/option.move:usage}} +{{#include ../../../packages/samples/sources/basic-syntax/option.move:usage}} ``` diff --git a/book/basic-syntax/ownership-and-scope.md b/book/src/basic-syntax/ownership-and-scope.md similarity index 100% rename from book/basic-syntax/ownership-and-scope.md rename to book/src/basic-syntax/ownership-and-scope.md diff --git a/book/basic-syntax/primitive-types.md b/book/src/basic-syntax/primitive-types.md similarity index 100% rename from book/basic-syntax/primitive-types.md rename to book/src/basic-syntax/primitive-types.md diff --git a/book/basic-syntax/references.md b/book/src/basic-syntax/references.md similarity index 88% rename from book/basic-syntax/references.md rename to book/src/basic-syntax/references.md index fe58f6c5..c0a29f8c 100644 --- a/book/basic-syntax/references.md +++ b/book/src/basic-syntax/references.md @@ -33,9 +33,9 @@ The initial layout of the metro pass application is simple. We define the `Card` ```move module book::metro_pass { -{{#include ../../packages/samples/sources/basic-syntax/references.move:header}} +{{#include ../../../packages/samples/sources/basic-syntax/references.move:header}} -{{#include ../../packages/samples/sources/basic-syntax/references.move:new}} +{{#include ../../../packages/samples/sources/basic-syntax/references.move:new}} } ``` @@ -48,7 +48,7 @@ References are a way to *show* a value to a function without giving up the owner To do so, in the function signature, we use the `&` symbol to indicate that we are passing a reference to the value, not the value itself. ```move -{{#include ../../packages/samples/sources/basic-syntax/references.move:immutable}} +{{#include ../../../packages/samples/sources/basic-syntax/references.move:immutable}} ``` Now the function can't take the ownership of the card, and it can't spend the rides. But it can read its value. Worth noting, that a signature like this makes it impossible to call the function without a Card at all. This is an important property which allows the [Capability Pattern](./../programmability/capability.md) which we will cover in the next chapters. @@ -58,7 +58,7 @@ Now the function can't take the ownership of the card, and it can't spend the ri In some cases, we want to allow the function to change the value of the Card. For example, when we use the Card at the turnstile, we want to spend a ride. To implement it, we use the `&mut` keyword in the function signature. ```move -{{#include ../../packages/samples/sources/basic-syntax/references.move:mutable}} +{{#include ../../../packages/samples/sources/basic-syntax/references.move:mutable}} ``` As you can see in the function body, the `&mut` reference allows mutating the value, and the function can spend the rides. @@ -68,7 +68,7 @@ As you can see in the function body, the `&mut` reference allows mutating the va Lastly, let's give an illustration of what happens when we pass the value itself to the function. In this case, the function takes the ownership of the value, and the original scope can no longer use it. The owner of the Card can recycle it, and, hence, lose the ownership. ```move -{{#include ../../packages/samples/sources/basic-syntax/references.move:move}} +{{#include ../../../packages/samples/sources/basic-syntax/references.move:move}} ``` In the `recycle` function, the Card is *taken by value* and can be unpacked and destroyed. The original scope can't use it anymore. @@ -78,7 +78,7 @@ In the `recycle` function, the Card is *taken by value* and can be unpacked and To illustrate the full flow of the application, let's put all the pieces together in a test. ```move -{{#include ../../packages/samples/sources/basic-syntax/references.move:move_2024}} +{{#include ../../../packages/samples/sources/basic-syntax/references.move:move_2024}} ``` @@ -99,6 +99,6 @@ To illustrate the full flow of the application, let's put all the pieces togethe Here's the test from this page written with the Move 2024 syntax: ```move -{{#include ../../packages/samples/sources/basic-syntax/references.move:move_2024}} +{{#include ../../../packages/samples/sources/basic-syntax/references.move:move_2024}} ``` --> diff --git a/book/basic-syntax/standard-library.md b/book/src/basic-syntax/standard-library.md similarity index 100% rename from book/basic-syntax/standard-library.md rename to book/src/basic-syntax/standard-library.md diff --git a/book/basic-syntax/string.md b/book/src/basic-syntax/string.md similarity index 86% rename from book/basic-syntax/string.md rename to book/src/basic-syntax/string.md index 9b84532f..cce7e4e6 100644 --- a/book/basic-syntax/string.md +++ b/book/src/basic-syntax/string.md @@ -16,7 +16,7 @@ TODO: No matter which type of string you use, it is important to know that strings are just bytes. The wrappers provided by the `string` and `ascii` modules are just that: wrappers. They do provide extra checks and functionality than a vector of bytes, but under the hood, they are just vectors of bytes. ```move -{{#include ../../packages/samples/sources/basic-syntax/string.move:custom}} +{{#include ../../../packages/samples/sources/basic-syntax/string.move:custom}} ``` Both standard types provide conversions from and to vectors of bytes. @@ -26,7 +26,7 @@ Both standard types provide conversions from and to vectors of bytes. While there are two types of strings in the standard library, the `string` module should be considered the default. It has native implementations of many common operations, and hence is more efficient than the `ascii` module. To create a string or perform operations on it, you must import the `string` module: ```move -{{#include ../../packages/samples/sources/basic-syntax/string.move:utf8}} +{{#include ../../../packages/samples/sources/basic-syntax/string.move:utf8}} ``` ## Safe UTF-8 Operations @@ -36,7 +36,7 @@ The default `utf8` method is potentially unsafe, as it does not check that the b > The `try_*` pattern is used throughout the standard library to indicate that a function may fail. For more information, see the [Error Handling](./error-handling.md) section. ```move -{{#include ../../packages/samples/sources/basic-syntax/string.move:safe_utf8}} +{{#include ../../../packages/samples/sources/basic-syntax/string.move:safe_utf8}} ``` ## ASCII Strings @@ -44,7 +44,7 @@ The default `utf8` method is potentially unsafe, as it does not check that the b TODO: ASCII strings ```move -{{#include ../../packages/samples/sources/basic-syntax/string.move:ascii}} +{{#include ../../../packages/samples/sources/basic-syntax/string.move:ascii}} ``` ## Summary diff --git a/book/basic-syntax/struct-methods.md b/book/src/basic-syntax/struct-methods.md similarity index 89% rename from book/basic-syntax/struct-methods.md rename to book/src/basic-syntax/struct-methods.md index cbf7fa97..870f0a30 100644 --- a/book/basic-syntax/struct-methods.md +++ b/book/src/basic-syntax/struct-methods.md @@ -9,7 +9,7 @@ If the first argument of a function is a struct internal to the module, then the When a module is imported, the methods are automatically associated with the struct. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct-methods.move:hero}} +{{#include ../../../packages/samples/sources/basic-syntax/struct-methods.move:hero}} ``` ## Method Aliases @@ -30,7 +30,7 @@ public use fun function_path as Type.method_name; In the example below, we changed the `hero` module and added another type - `Villain`. Both `Hero` and `Villain` have similar field names and methods. And to avoid name conflicts, we prefixed methods with `hero_` and `villain_` respectively. However, we can create aliases for these methods so that they can be called on the instances of the structs without the prefix. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct-methods.move:hero_and_villain}} +{{#include ../../../packages/samples/sources/basic-syntax/struct-methods.move:hero_and_villain}} ``` As you can see, in the test function, we called the `health` method on the instances of `Hero` and `Villain` without the prefix. The compiler will automatically associate the methods with the structs. @@ -40,5 +40,5 @@ As you can see, in the test function, we called the `health` method on the insta It is also possible to associate a function defined in another module with a struct from the current module. Following the same approach, we can create an alias for the method defined in another module. Let's use the `bcs::to_bytes` method from the [Standard Library](./standard-library.md) and associate it with the `Hero` struct. It will allow serializing the `Hero` struct to a vector of bytes. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct-methods.move:hero_to_bytes}} +{{#include ../../../packages/samples/sources/basic-syntax/struct-methods.move:hero_to_bytes}} ``` diff --git a/book/basic-syntax/struct.md b/book/src/basic-syntax/struct.md similarity index 86% rename from book/basic-syntax/struct.md rename to book/src/basic-syntax/struct.md index f082f2b2..44b5c149 100644 --- a/book/basic-syntax/struct.md +++ b/book/src/basic-syntax/struct.md @@ -9,7 +9,7 @@ To define a custom type, you can use the `struct` keyword followed by the name o > Note: Move does not support recursive structs, meaning a struct cannot contain itself as a field. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct.move:def}} +{{#include ../../../packages/samples/sources/basic-syntax/struct.move:def}} ``` In the example above, we define a `Record` struct with five fields. The `title` field is of type `String`, the `artist` field is of type `Artist`, the `year` field is of type `u16`, the `is_debut` field is of type `bool`, and the `edition` field is of type `Option`. The `edition` field is of type `Option` to represent that the edition is optional. @@ -23,7 +23,7 @@ Structs are private by default, meaning they cannot be imported and used outside We described how struct *definition* works. Now let's see how to initialize a struct and use it. A struct can be initialized using the `struct_name { field1: value1, field2: value2, ... }` syntax. The fields can be initialized in any order, and all of the fields must be set. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct.move:pack}} +{{#include ../../../packages/samples/sources/basic-syntax/struct.move:pack}} ``` In the example above, we create an instance of the `Artist` struct and set the `name` field to a string "The Beatles". @@ -31,7 +31,7 @@ In the example above, we create an instance of the `Artist` struct and set the ` To access the fields of a struct, you can use the `.` operator followed by the field name. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct.move:access}} +{{#include ../../../packages/samples/sources/basic-syntax/struct.move:access}} ``` Only module defining the struct can access its fields (both mutably and immutably). So the above code should be in the same module as the `Artist` struct. @@ -42,7 +42,7 @@ Struct fields are private and can be accessed only by the module defining the st ```move # anchor: access -{{#include ../../packages/samples/sources/basic-syntax/struct.move:access}} +{{#include ../../../packages/samples/sources/basic-syntax/struct.move:access}} ``` --> @@ -51,11 +51,11 @@ Struct fields are private and can be accessed only by the module defining the st Structs are non-discardable by default, meaning that the initiated struct value must be used: either stored or *unpacked*. Unpacking a struct means deconstructing it into its fields. This is done using the `let` keyword followed by the struct name and the field names. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct.move:unpack}} +{{#include ../../../packages/samples/sources/basic-syntax/struct.move:unpack}} ``` In the example above we unpack the `Artist` struct and create a new variable `name` with the value of the `name` field. Because the variable is not used, the compiler will raise a warning. To suppress the warning, you can use the underscore `_` to indicate that the variable is intentionally unused. ```move -{{#include ../../packages/samples/sources/basic-syntax/struct.move:unpack_ignore}} +{{#include ../../../packages/samples/sources/basic-syntax/struct.move:unpack_ignore}} ``` diff --git a/book/basic-syntax/testing.md b/book/src/basic-syntax/testing.md similarity index 100% rename from book/basic-syntax/testing.md rename to book/src/basic-syntax/testing.md diff --git a/book/basic-syntax/type-reflection.md b/book/src/basic-syntax/type-reflection.md similarity index 91% rename from book/basic-syntax/type-reflection.md rename to book/src/basic-syntax/type-reflection.md index 31ceca8a..eed50508 100644 --- a/book/basic-syntax/type-reflection.md +++ b/book/src/basic-syntax/type-reflection.md @@ -9,7 +9,7 @@ Type reflection is implemented in the [Standard Library](./standard-library.md) The module is pretty straightforward, and operations allowed on the result are limited to getting a string representation and extracting the module and address of the type. ```move -{{#include ../../packages/samples/sources/basic-syntax/type-reflection.move:main}} +{{#include ../../../packages/samples/sources/basic-syntax/type-reflection.move:main}} ``` ## Further reading diff --git a/book/basic-syntax/vector.md b/book/src/basic-syntax/vector.md similarity index 86% rename from book/basic-syntax/vector.md rename to book/src/basic-syntax/vector.md index 37803dbd..8bdb8f46 100644 --- a/book/basic-syntax/vector.md +++ b/book/src/basic-syntax/vector.md @@ -7,7 +7,7 @@ Vectors are a native way to store collections of elements in Move. They are simi The `vector` type is defined using the `vector` keyword followed by the type of the elements in angle brackets. The type of the elements can be any valid Move type, including other vectors. Move has a vector literal syntax that allows you to create vectors using the `vector` keyword followed by square brackets containing the elements (or no elements for an empty vector). ```move -{{#include ../../packages/samples/sources/basic-syntax/vector.move:literal}} +{{#include ../../../packages/samples/sources/basic-syntax/vector.move:literal}} ``` The `vector` type is a built-in type in Move, and does not need to be imported from a module. However, vector operations are defined in the `std::vector` module, and you need to import the module to use them. @@ -23,7 +23,7 @@ The standard library provides methods to manipulate vectors. The following are s - `remove`: Removes an element at a given index. ```move -{{#include ../../packages/samples/sources/basic-syntax/vector.move:methods}} +{{#include ../../../packages/samples/sources/basic-syntax/vector.move:methods}} ``` ## Destroying a Vector of non-droppable types @@ -31,5 +31,5 @@ The standard library provides methods to manipulate vectors. The following are s A vector of non-droppable types cannot be discarded. If you define a vector of types without `drop` ability, the vector value cannot be ignored. However, if the vector is empty, compiler requires an explicit call to `destroy_empty` function. ```move -{{#include ../../packages/samples/sources/basic-syntax/vector.move:no_drop}} +{{#include ../../../packages/samples/sources/basic-syntax/vector.move:no_drop}} ``` diff --git a/book/basic-syntax/visibility.md b/book/src/basic-syntax/visibility.md similarity index 100% rename from book/basic-syntax/visibility.md rename to book/src/basic-syntax/visibility.md diff --git a/book/before-we-begin/README.md b/book/src/before-we-begin/README.md similarity index 100% rename from book/before-we-begin/README.md rename to book/src/before-we-begin/README.md diff --git a/book/before-we-begin/ide-support.md b/book/src/before-we-begin/ide-support.md similarity index 100% rename from book/before-we-begin/ide-support.md rename to book/src/before-we-begin/ide-support.md diff --git a/book/before-we-begin/install-sui.md b/book/src/before-we-begin/install-sui.md similarity index 100% rename from book/before-we-begin/install-sui.md rename to book/src/before-we-begin/install-sui.md diff --git a/book/before-we-begin/move-2024.md b/book/src/before-we-begin/move-2024.md similarity index 100% rename from book/before-we-begin/move-2024.md rename to book/src/before-we-begin/move-2024.md diff --git a/book/concepts/README.md b/book/src/concepts/README.md similarity index 100% rename from book/concepts/README.md rename to book/src/concepts/README.md diff --git a/book/concepts/address.md b/book/src/concepts/address.md similarity index 100% rename from book/concepts/address.md rename to book/src/concepts/address.md diff --git a/book/concepts/manifest.md b/book/src/concepts/manifest.md similarity index 100% rename from book/concepts/manifest.md rename to book/src/concepts/manifest.md diff --git a/book/concepts/modules.md b/book/src/concepts/modules.md similarity index 100% rename from book/concepts/modules.md rename to book/src/concepts/modules.md diff --git a/book/concepts/object-model.md b/book/src/concepts/object-model.md similarity index 100% rename from book/concepts/object-model.md rename to book/src/concepts/object-model.md diff --git a/book/concepts/packages.md b/book/src/concepts/packages.md similarity index 100% rename from book/concepts/packages.md rename to book/src/concepts/packages.md diff --git a/book/concepts/user-interaction.md b/book/src/concepts/user-interaction.md similarity index 100% rename from book/concepts/user-interaction.md rename to book/src/concepts/user-interaction.md diff --git a/book/concepts/what-is-a-transaction.md b/book/src/concepts/what-is-a-transaction.md similarity index 100% rename from book/concepts/what-is-a-transaction.md rename to book/src/concepts/what-is-a-transaction.md diff --git a/book/concepts/what-is-an-account.md b/book/src/concepts/what-is-an-account.md similarity index 100% rename from book/concepts/what-is-an-account.md rename to book/src/concepts/what-is-an-account.md diff --git a/book/foreword.md b/book/src/foreword.md similarity index 100% rename from book/foreword.md rename to book/src/foreword.md diff --git a/book/guides/2024-migration-guide.md b/book/src/guides/2024-migration-guide.md similarity index 100% rename from book/guides/2024-migration-guide.md rename to book/src/guides/2024-migration-guide.md diff --git a/book/guides/README.md b/book/src/guides/README.md similarity index 100% rename from book/guides/README.md rename to book/src/guides/README.md diff --git a/book/guides/better-error-handling.md b/book/src/guides/better-error-handling.md similarity index 100% rename from book/guides/better-error-handling.md rename to book/src/guides/better-error-handling.md diff --git a/book/guides/building-against-limits.md b/book/src/guides/building-against-limits.md similarity index 100% rename from book/guides/building-against-limits.md rename to book/src/guides/building-against-limits.md diff --git a/book/guides/debugging.md b/book/src/guides/debugging.md similarity index 100% rename from book/guides/debugging.md rename to book/src/guides/debugging.md diff --git a/book/guides/open-sourcing-libraries.md b/book/src/guides/open-sourcing-libraries.md similarity index 100% rename from book/guides/open-sourcing-libraries.md rename to book/src/guides/open-sourcing-libraries.md diff --git a/book/guides/testing.md b/book/src/guides/testing.md similarity index 100% rename from book/guides/testing.md rename to book/src/guides/testing.md diff --git a/book/guides/upgradeability-practices.md b/book/src/guides/upgradeability-practices.md similarity index 100% rename from book/guides/upgradeability-practices.md rename to book/src/guides/upgradeability-practices.md diff --git a/book/hello-sui/README.md b/book/src/hello-sui/README.md similarity index 100% rename from book/hello-sui/README.md rename to book/src/hello-sui/README.md diff --git a/book/hello-sui/hello-sui.md b/book/src/hello-sui/hello-sui.md similarity index 100% rename from book/hello-sui/hello-sui.md rename to book/src/hello-sui/hello-sui.md diff --git a/book/hello-sui/module-structure.md b/book/src/hello-sui/module-structure.md similarity index 100% rename from book/hello-sui/module-structure.md rename to book/src/hello-sui/module-structure.md diff --git a/book/history.md b/book/src/history.md similarity index 100% rename from book/history.md rename to book/src/history.md diff --git a/book/introduction.md b/book/src/introduction.md similarity index 100% rename from book/introduction.md rename to book/src/introduction.md diff --git a/book/object/README.md b/book/src/object/README.md similarity index 100% rename from book/object/README.md rename to book/src/object/README.md diff --git a/book/object/key-ability.md b/book/src/object/key-ability.md similarity index 100% rename from book/object/key-ability.md rename to book/src/object/key-ability.md diff --git a/book/object/shared-state.md b/book/src/object/shared-state.md similarity index 100% rename from book/object/shared-state.md rename to book/src/object/shared-state.md diff --git a/book/object/store-ability.md b/book/src/object/store-ability.md similarity index 100% rename from book/object/store-ability.md rename to book/src/object/store-ability.md diff --git a/book/object/transfer-restrictions.md b/book/src/object/transfer-restrictions.md similarity index 100% rename from book/object/transfer-restrictions.md rename to book/src/object/transfer-restrictions.md diff --git a/book/object/transfer-to-object.md b/book/src/object/transfer-to-object.md similarity index 100% rename from book/object/transfer-to-object.md rename to book/src/object/transfer-to-object.md diff --git a/book/object/true-ownership.md b/book/src/object/true-ownership.md similarity index 100% rename from book/object/true-ownership.md rename to book/src/object/true-ownership.md diff --git a/book/object/what-is-an-object.md b/book/src/object/what-is-an-object.md similarity index 100% rename from book/object/what-is-an-object.md rename to book/src/object/what-is-an-object.md diff --git a/book/programmability/README.md b/book/src/programmability/README.md similarity index 100% rename from book/programmability/README.md rename to book/src/programmability/README.md diff --git a/book/programmability/abstract-class.md b/book/src/programmability/abstract-class.md similarity index 100% rename from book/programmability/abstract-class.md rename to book/src/programmability/abstract-class.md diff --git a/book/programmability/authorization-patterns.md b/book/src/programmability/authorization-patterns.md similarity index 100% rename from book/programmability/authorization-patterns.md rename to book/src/programmability/authorization-patterns.md diff --git a/book/programmability/balance-and-coin.md b/book/src/programmability/balance-and-coin.md similarity index 100% rename from book/programmability/balance-and-coin.md rename to book/src/programmability/balance-and-coin.md diff --git a/book/programmability/bcs.md b/book/src/programmability/bcs.md similarity index 91% rename from book/programmability/bcs.md rename to book/src/programmability/bcs.md index 8d2edf14..bd2a4c51 100644 --- a/book/programmability/bcs.md +++ b/book/src/programmability/bcs.md @@ -17,5 +17,5 @@ public native fun to_bytes(t: &T): vector; ```move -{{#include ../../packages/samples/sources/programmability/bcs.move:using_bcs}} +{{#include ../../../packages/samples/sources/programmability/bcs.move:using_bcs}} ``` diff --git a/book/programmability/capability.md b/book/src/programmability/capability.md similarity index 89% rename from book/programmability/capability.md rename to book/src/programmability/capability.md index 8f0700b9..6d323745 100644 --- a/book/programmability/capability.md +++ b/book/src/programmability/capability.md @@ -9,7 +9,7 @@ In the [Sui Object Model](./../concepts/object-model.md), capabilities are repre > There's a convention to name capabilities with the `Cap` suffix, for example, `AdminCap` or `KioskOwnerCap`. ```move -{{#include ../../packages/samples/sources/programmability/capability.move:main}} +{{#include ../../../packages/samples/sources/programmability/capability.move:main}} ``` ## Using `init` for Admin Capability @@ -17,7 +17,7 @@ In the [Sui Object Model](./../concepts/object-model.md), capabilities are repre A very common practice is to create a single `AdminCap` object on package publish. This way, the 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.move:admin_cap}} ``` ## Address check vs Capability @@ -27,13 +27,13 @@ Utilizing objects as capabilities is a relatively new concept in blockchain prog Let's look at how the `new` function that creates a user would look like if it was using the address check: ```move -{{#include ../../packages/samples/sources/programmability/capability.move:with_address}} +{{#include ../../../packages/samples/sources/programmability/capability.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.move:with_capability}} ``` Using capabilities has several advantages over the address check: diff --git a/book/programmability/collections.md b/book/src/programmability/collections.md similarity index 89% rename from book/programmability/collections.md rename to book/src/programmability/collections.md index f448e16b..f59bcb87 100644 --- a/book/programmability/collections.md +++ b/book/src/programmability/collections.md @@ -19,7 +19,7 @@ While we have previously covered the `vector` type in the [vector section](./../ `VecSet` is a collection type that stores a set of unique items. It is similar to a `vector`, but it does not allow duplicate items. This makes it useful for storing a collection of unique items, such as a list of unique IDs or addresses. ```move -{{#include ../../packages/samples/sources/programmability/collections.move:vec_set}} +{{#include ../../../packages/samples/sources/programmability/collections.move:vec_set}} ``` ## VecMap @@ -29,5 +29,5 @@ While we have previously covered the `vector` type in the [vector section](./../ Keys in a `VecMap` are unique, and each key can only be associated with a single value. If you try to insert a key-value pair with a key that already exists in the map, the old value will be replaced with the new value. ```move -{{#include ../../packages/samples/sources/programmability/collections.move:vec_map}} +{{#include ../../../packages/samples/sources/programmability/collections.move:vec_map}} ``` diff --git a/book/programmability/cryptography-and-hashing.md b/book/src/programmability/cryptography-and-hashing.md similarity index 100% rename from book/programmability/cryptography-and-hashing.md rename to book/src/programmability/cryptography-and-hashing.md diff --git a/book/programmability/display.md b/book/src/programmability/display.md similarity index 95% rename from book/programmability/display.md rename to book/src/programmability/display.md index ccdb80a2..6c197ac7 100644 --- a/book/programmability/display.md +++ b/book/src/programmability/display.md @@ -7,7 +7,7 @@ Objects on Sui are explicit in their structure and behavior and can be displayed Historically, there were different attempts to agree on a standard structure of an object so it can be displayed in a user interface. One of the approaches was to define certain fields in the object struct which, when present, would be used in the UI. This approach was not flexible enough and required developers to define the same fields in every object, and sometimes the fields did not make sense for the object. ```move -{{#include ../../packages/samples/sources/programmability/display.move:background}} +{{#include ../../../packages/samples/sources/programmability/display.move:background}} ``` If any of the fields contained static data, it would be duplicated in every object. And, since Move does not have interfaces, it is not possible to know if an object has a specific field without "manually" checking the object's type, which makes the client fetching more complex. @@ -21,7 +21,7 @@ Another important feature of Sui Display is the ability to define templates and > The Object Display is natively supported by the Sui Fullnode, and the client can fetch the display metadata for any object if the object type has a Display associated with it. ```move -{{#include ../../packages/samples/sources/programmability/display.move:hero}} +{{#include ../../../packages/samples/sources/programmability/display.move:hero}} ``` ## Creator Privilege @@ -67,7 +67,7 @@ The [Publisher](./publisher.md) object is required to a new Display, since it se Currently, Display supports simple string interpolation and can use struct fields (and paths) in its templates. The syntax is trivial - `{path}` is replaced with the value of the field at the path. The path is a dot-separated list of field names, starting from the root object in case of nested fields. ```move -{{#include ../../packages/samples/sources/programmability/display.move:nested}} +{{#include ../../../packages/samples/sources/programmability/display.move:nested}} ``` The Display for the type `LittlePony` above could be defined as follows: diff --git a/book/programmability/dynamic-collections.md b/book/src/programmability/dynamic-collections.md similarity index 88% rename from book/programmability/dynamic-collections.md rename to book/src/programmability/dynamic-collections.md index 99c97fde..dfbde375 100644 --- a/book/programmability/dynamic-collections.md +++ b/book/src/programmability/dynamic-collections.md @@ -50,12 +50,12 @@ Due to Bag storing any types, the extra methods it offers is: Used as a struct field: ```move -{{#include ../../packages/samples/sources/programmability/dynamic-collections.move:bag_struct}} +{{#include ../../../packages/samples/sources/programmability/dynamic-collections.move:bag_struct}} ``` Using the Bag: ```move -{{#include ../../packages/samples/sources/programmability/dynamic-collections.move:bag_usage}} +{{#include ../../../packages/samples/sources/programmability/dynamic-collections.move:bag_usage}} ``` ## ObjectBag @@ -78,12 +78,12 @@ public struct Table has key, s Used as a struct field: ```move -{{#include ../../packages/samples/sources/programmability/dynamic-collections.move:table_struct}} +{{#include ../../../packages/samples/sources/programmability/dynamic-collections.move:table_struct}} ``` Using the Table: ```move -{{#include ../../packages/samples/sources/programmability/dynamic-collections.move:table_usage}} +{{#include ../../../packages/samples/sources/programmability/dynamic-collections.move:table_usage}} ``` ## ObjectTable diff --git a/book/programmability/dynamic-fields.md b/book/src/programmability/dynamic-fields.md similarity index 92% rename from book/programmability/dynamic-fields.md rename to book/src/programmability/dynamic-fields.md index a40b926b..6f252522 100644 --- a/book/programmability/dynamic-fields.md +++ b/book/src/programmability/dynamic-fields.md @@ -31,7 +31,7 @@ As the definition shows, dynamic fields are stored in an internal `Field` object The methods available for dynamic fields are straightforward: a field can be added with `add`, removed with `remove`, and read with `borrow` and `borrow_mut`. Additionally, the `exists_` method can be used to check if a field exists (for stricter checks with type, there is an `exists_with_type` method). ```move -{{#include ../../packages/samples/sources/programmability/dynamic-fields.move:usage}} +{{#include ../../../packages/samples/sources/programmability/dynamic-fields.move:usage}} } ``` @@ -46,7 +46,7 @@ And the last important property of dynamic fields we should highlight is that th Dynamic fields allow objects to carry data of any type, including those defined in other modules. This is possible due to their generic nature and relatively weak constraints on the type parameters. Let's illustrate this by attaching a few different values to a `Character` object. ```move -{{#include ../../packages/samples/sources/programmability/dynamic-fields.move:foreign_types}} +{{#include ../../../packages/samples/sources/programmability/dynamic-fields.move:foreign_types}} ``` In this example we showed how different types can be used for both *name* and the *value* of a dynamic field. The `String` is attached via a `vector` name, the `u64` is attached via a `u32` name, and the `bool` is attached via a `bool` name. Anything is possible with dynamic fields! @@ -58,7 +58,7 @@ In this example we showed how different types can be used for both *name* and th The `object::delete()` function, which is used to delete a UID, does not track the dynamic fields, and cannot prevent dynamic fields from becoming orphaned. Once the parent UID is deleted, the dynamic fields are not automatically deleted, and they become orphaned. This means that the dynamic fields are still stored in the blockchain, but they will never become accessible again. ```move -{{#include ../../packages/samples/sources/programmability/dynamic-fields.move:orphan_fields}} +{{#include ../../../packages/samples/sources/programmability/dynamic-fields.move:orphan_fields}} ``` Orphaned objects are not a subject to storage rebate, and the storage fees will remain unclaimed. One way to avoid orphaned dynamic fields during unpacking on an object is to return the `UID` and store it somewhere temporarily until the dynamic fields are removed and handled properly. @@ -68,13 +68,13 @@ Orphaned objects are not a subject to storage rebate, and the storage fees will In the examples above, we used primitive types as field names since they have the required set of abilities. But dynamic fields get even more interesting when we use custom types as field names. This allows for a more structured way of storing data, and also allows for protecting the field names from being accessed by other modules. ```move -{{#include ../../packages/samples/sources/programmability/dynamic-fields.move:custom_type}} +{{#include ../../../packages/samples/sources/programmability/dynamic-fields.move:custom_type}} ``` Two field names that we defined above are `AccessoryKey` and `MetadataKey`. The `AccessoryKey` has a `String` field in it, hence it can be used multiple times with different `name` values. The `MetadataKey` is an empty key, and can be attached only once. ```move -{{#include ../../packages/samples/sources/programmability/dynamic-fields.move:custom_type_usage}} +{{#include ../../../packages/samples/sources/programmability/dynamic-fields.move:custom_type_usage}} ``` As you can see, custom types do work as field names but as long as they can be *constructed* by the module, in other words - if they are *internal* to the module and defined in it. This limitation on struct packing can open up new ways in the design of the application. @@ -92,7 +92,7 @@ Mutable access to `UID` is a security risk. Exposing `UID` of your type as a mut Because dynamic fields are attached to `UID`s, their usage in other modules depends on whether the `UID` can be accessed. By default struct visibility protects the `id` field and won't let other modules access it directly. However, if there's a public accessor method that returns a reference to `UID`, dynamic fields can be read in other modules. ```move -{{#include ../../packages/samples/sources/programmability/dynamic-fields.move:exposed_uid}} +{{#include ../../../packages/samples/sources/programmability/dynamic-fields.move:exposed_uid}} ``` In the example above, we show how to expose the `UID` of a `Character` object. This solution may work for some applications, however, it is imporant to remember that exposed `UID` allows reading *any* dynamic field attached to the object. @@ -100,7 +100,7 @@ In the example above, we show how to expose the `UID` of a `Character` object. T If you need to expose the `UID` only within the package, use a restrictive visibility, like `public(package)`, or even better - use more specific accessor methods that would allow only reading specific fields. ```move -{{#include ../../packages/samples/sources/programmability/dynamic-fields.move:exposed_uid_measures}} +{{#include ../../../packages/samples/sources/programmability/dynamic-fields.move:exposed_uid_measures}} ``` ## Dynamic Fields vs Fields diff --git a/book/programmability/dynamic-object-fields.md b/book/src/programmability/dynamic-object-fields.md similarity index 97% rename from book/programmability/dynamic-object-fields.md rename to book/src/programmability/dynamic-object-fields.md index 8a2ac753..439f2b3b 100644 --- a/book/programmability/dynamic-object-fields.md +++ b/book/src/programmability/dynamic-object-fields.md @@ -56,7 +56,7 @@ The main difference between dynamic fields and dynamic object fields is that the > The relaxed requirement for wrapping keeps the object available for off-chain discovery via its ID. However, this property may not be outstanding if wrapped object indexing is implemented, making the dynamic object fields a redundant feature. ```move -{{#include ../../packages/samples/sources/programmability/dynamic-object-fields.move:usage}} +{{#include ../../../packages/samples/sources/programmability/dynamic-object-fields.move:usage}} ``` ## Pricing Differences diff --git a/book/programmability/epoch-and-time.md b/book/src/programmability/epoch-and-time.md similarity index 90% rename from book/programmability/epoch-and-time.md rename to book/src/programmability/epoch-and-time.md index 8bc0ed50..3c7cce46 100644 --- a/book/programmability/epoch-and-time.md +++ b/book/src/programmability/epoch-and-time.md @@ -9,13 +9,13 @@ Epochs are used to separate the system into operational periods. During an epoch Epoch can be read from the [transaction context](./transaction-context.md): ```move -{{#include ../../packages/samples/sources/programmability/epoch-and-time.move:epoch}} +{{#include ../../../packages/samples/sources/programmability/epoch-and-time.move:epoch}} ``` It is also possible to get the unix timestamp of the epoch start: ```move -{{#include ../../packages/samples/sources/programmability/epoch-and-time.move:epoch_start}} +{{#include ../../../packages/samples/sources/programmability/epoch-and-time.move:epoch_start}} ``` Normally, epochs are used in staking and system operations, however, in custom scenarios they can be used to emulate 24h periods. They are critical if an application relies on the staking logic or needs to know the current validator set. @@ -49,7 +49,7 @@ struct Clock has key { There is only one public function available in the `Clock` module - `timestamp_ms`. It returns the current time in milliseconds since the Unix Epoch. ```move -{{#include ../../packages/samples/sources/programmability/epoch-and-time.move:clock}} +{{#include ../../../packages/samples/sources/programmability/epoch-and-time.move:clock}} ``` + +## The Four Abilities + +The four abilities are: + +- [`copy`](#copy) + - Allows values of types with this ability to be copied. +- [`drop`](#drop) + - Allows values of types with this ability to be popped/dropped. +- [`store`](#store) + - Allows values of types with this ability to exist inside a value in storage. + - For Sui, `store` controls what data can be stored inside of an [object](./abilities/object.md). + `store` also controls what types can be transferred outside of their defining module. +- [`key`](#key) + - Allows the type to serve as a "key" for storage. Ostensibly this means the value can be a + top-level value in storage; in other words, it does not need to be contained in another value to + be in storage. + - For Sui, `key` is used to signify an [object](./abilities/object.md). + +### `copy` + +The `copy` ability allows values of types with that ability to be copied. It gates the ability to +copy values out of local variables with the [`copy`](./variables.md#move-and-copy) operator and to +copy values via references with +[dereference `*e`](./primitive-types/references.md#reading-and-writing-through-references). + +If a value has `copy`, all values contained inside of that value have `copy`. + +### `drop` + +The `drop` ability allows values of types with that ability to be dropped. By dropped, we mean that +value is not transferred and is effectively destroyed as the Move program executes. As such, this +ability gates the ability to ignore values in a multitude of locations, including: + +- not using the value in a local variable or parameter +- not using the value in a [sequence via `;`](./variables.md#expression-blocks) +- overwriting values in variables in [assignments](./variables.md#assignments) +- overwriting values via references when + [writing `*e1 = e2`](./primitive-types/references.md#reading-and-writing-through-references). + +If a value has `drop`, all values contained inside of that value have `drop`. + +### `store` + +The `store` ability allows values of types with this ability to exist inside of a value in storage, +_but_ not necessarily as a top-level value in storage. This is the only ability that does not +directly gate an operation. Instead it gates the existence in storage when used in tandem with +`key`. + +If a value has `store`, all values contained inside of that value have `store`. + +For Sui, `store` serves double duty. It controls what values can appear inside of an +[object](./abilities/object.md), and what objects can be +[transferred](./abilities/object.md#transfer-rules) outside of their defining module. + +### `key` + +The `key` ability allows the type to serve as a key for storage operations as defined by the +deployment of Move. While it is specific per Move instance, it serves to gates all storage +operations, so in order for a type to be used with storage primitives, the type must have the `key` +ability. + +If a value has `key`, all values contained inside of that value have `store`. This is the only +ability with this sort of asymmetry. + +For Sui, `key` is used to signify an [object](./abilities/object.md). + +## Builtin Types + +All primitive, builtin types have `copy`, `drop`, and `store`. + +- `bool`, `u8`, `u16`, `u32`, `u64`, `u128`, `u256`, and `address` all have `copy`, `drop`, and + `store`. +- `vector` may have `copy`, `drop`, and `store` depending on the abilities of `T`. + - See [Conditional Abilities and Generic Types](#conditional-abilities-and-generic-types) for more + details. +- Immutable references `&` and mutable references `&mut` both have `copy` and `drop`. + - This refers to copying and dropping the reference itself, not what they refer to. + - References cannot appear in global storage, hence they do not have `store`. + +Note that none of the primitive types have `key`, meaning none of them can be used directly with +storage operations. + +## Annotating Structs + +To declare that a `struct` has an ability, it is declared with `has ` after the struct name +and either before or after the fields. For example: + +```move +public struct Ignorable has drop { f: u64 } +public struct Pair has copy, drop, store { x: u64, y: u64 } +public struct MyVec(vector) has copy, drop, store; +``` + +In this case: `Ignorable` has the `drop` ability. `Pair` and `MyVec` both have `copy`, `drop`, and +`store`. + +All of these abilities have strong guarantees over these gated operations. The operation can be +performed on the value only if it has that ability; even if the value is deeply nested inside of +some other collection! + +As such: when declaring a struct’s abilities, certain requirements are placed on the fields. All +fields must satisfy these constraints. These rules are necessary so that structs satisfy the +reachability rules for the abilities given above. If a struct is declared with the ability... + +- `copy`, all fields must have `copy`. +- `drop`, all fields must have `drop`. +- `store`, all fields must have `store`. +- `key`, all fields must have `store`. + - `key` is the only ability currently that doesn’t require itself. + +For example: + +```move +// A struct without any abilities +public struct NoAbilities {} + +public struct WantsCopy has copy { + f: NoAbilities, // ERROR 'NoAbilities' does not have 'copy' +} +``` + +and similarly: + +```move +// A struct without any abilities +public struct NoAbilities {} + +public struct MyData has key { + f: NoAbilities, // Error 'NoAbilities' does not have 'store' +} +``` + +## Conditional Abilities and Generic Types + +When abilities are annotated on a generic type, not all instances of that type are guaranteed to +have that ability. Consider this struct declaration: + +```move +public struct Cup has copy, drop, store, key { item: T } +``` + +It might be very helpful if `Cup` could hold any type, regardless of its abilities. The type system +can _see_ the type parameter, so it should be able to remove abilities from `Cup` if it _sees_ a +type parameter that would violate the guarantees for that ability. + +This behavior might sound a bit confusing at first, but it might be more understandable if we think +about collection types. We could consider the builtin type `vector` to have the following type +declaration: + +```move +vector has copy, drop, store; +``` + +We want `vector`s to work with any type. We don't want separate `vector` types for different +abilities. So what are the rules we would want? Precisely the same that we would want with the field +rules above. So, it would be safe to copy a `vector` value only if the inner elements can be copied. +It would be safe to ignore a `vector` value only if the inner elements can be ignored/dropped. And, +it would be safe to put a `vector` in storage only if the inner elements can be in storage. + +To have this extra expressiveness, a type might not have all the abilities it was declared with +depending on the instantiation of that type; instead, the abilities a type will have depends on both +its declaration **and** its type arguments. For any type, type parameters are pessimistically +assumed to be used inside of the struct, so the abilities are only granted if the type parameters +meet the requirements described above for fields. Taking `Cup` from above as an example: + +- `Cup` has the ability `copy` only if `T` has `copy`. +- It has `drop` only if `T` has `drop`. +- It has `store` only if `T` has `store`. +- It has `key` only if `T` has `store`. + +Here are examples for this conditional system for each ability: + +### Example: conditional `copy` + +```move +public struct NoAbilities {} +public struct S has copy, drop { f: bool } +public struct Cup has copy, drop, store { item: T } + +fun example(c_x: Cup, c_s: Cup) { + // Valid, 'Cup' has 'copy' because 'u64' has 'copy' + let c_x2 = copy c_x; + // Valid, 'Cup' has 'copy' because 'S' has 'copy' + let c_s2 = copy c_s; +} + +fun invalid(c_account: Cup, c_n: Cup) { + // Invalid, 'Cup' does not have 'copy'. + // Even though 'Cup' was declared with copy, the instance does not have 'copy' + // because 'signer' does not have 'copy' + let c_account2 = copy c_account; + // Invalid, 'Cup' does not have 'copy' + // because 'NoAbilities' does not have 'copy' + let c_n2 = copy c_n; +} +``` + +### Example: conditional `drop` + +```move +public struct NoAbilities {} +public struct S has copy, drop { f: bool } +public struct Cup has copy, drop, store { item: T } + +fun unused() { + Cup { item: true }; // Valid, 'Cup' has 'drop' + Cup { item: S { f: false }}; // Valid, 'Cup' has 'drop' +} + +fun left_in_local(c_account: Cup): u64 { + let c_b = Cup { item: true }; + let c_s = Cup { item: S { f: false }}; + // Valid return: 'c_account', 'c_b', and 'c_s' have values + // but 'Cup', 'Cup', and 'Cup' have 'drop' + 0 +} + +fun invalid_unused() { + // Invalid, Cannot ignore 'Cup' because it does not have 'drop'. + // Even though 'Cup' was declared with 'drop', the instance does not have 'drop' + // because 'NoAbilities' does not have 'drop' + Cup { item: NoAbilities {} }; +} + +fun invalid_left_in_local(): u64 { + let n = Cup { item: NoAbilities {} }; + // Invalid return: 'c_n' has a value + // and 'Cup' does not have 'drop' + 0 +} +``` + +### Example: conditional `store` + +```move +public struct Cup has copy, drop, store { item: T } + +// 'MyInnerData is declared with 'store' so all fields need 'store' +struct MyInnerData has store { + yes: Cup, // Valid, 'Cup' has 'store' + // no: Cup, Invalid, 'Cup' does not have 'store' +} + +// 'MyData' is declared with 'key' so all fields need 'store' +struct MyData has key { + yes: Cup, // Valid, 'Cup' has 'store' + inner: Cup, // Valid, 'Cup' has 'store' + // no: Cup, Invalid, 'Cup' does not have 'store' +} +``` + +### Example: conditional `key` + +```move +public struct NoAbilities {} +public struct MyData has key { f: T } + +fun valid(addr: address) acquires MyData { + // Valid, 'MyData' has 'key' + transfer(addr, MyData { f: 0 }); +} + +fun invalid(addr: address) { + // Invalid, 'MyData' does not have 'key' + transfer(addr, MyData { f: NoAbilities {} }) + // Invalid, 'MyData' does not have 'key' + borrow(addr); + // Invalid, 'MyData' does not have 'key' + borrow_mut(addr); +} + +// Mock storage operation +native public fun transfer(addr: address, value: T); +``` diff --git a/reference/src/abilities/object.md b/reference/src/abilities/object.md new file mode 100644 index 00000000..ab846a1a --- /dev/null +++ b/reference/src/abilities/object.md @@ -0,0 +1,43 @@ +# Sui Objects + +For Sui, `key` is used to signify an _object_. Objects the only way to store data in Sui--allowing +the data to persist between transactions. + +For more details, see the Sui documentation on + +- [The Object Model](https://docs.sui.io/concepts/object-model) +- [Move Rules for Objects](https://docs.sui.io/concepts/sui-move-concepts#global-unique) +- [Transferring Objects](https://docs.sui.io/concepts/transfers) + +## Object Rules + +An object is a [`struct`](../structs.md) with the [`key`](../abilities.md#key) ability. The first +field of the struct must be `id: sui::object::UID`. This 32-byte field (a strongly typed wrapper +around an [`address`](../primitive-types/address.md)) is then used to uniquely identify the object. + +Note that since `sui::object::UID` has only the `store` ability (it does not have `copy` or `drop`), +no object has `copy` or `drop`. + +## Transfer Rules + +Objects can be have their ownership changed and transferred in the `sui::transfer` module. Many +functions in the module have "public" and "private" variant, where the "private" variant can only be +called inside of the module that defines the object's type. The "public" variants can be called only +if the object has `store`. + +For example if we had two objects `A` and `B` defined in the module `my_module`: + +``` +module a::my_module { + public struct A has key { + id: sui::object::UID, + } + public struct B has key, store { + id: sui::object::UID, + } +} +``` + +`A` can only be transferred using the `sui::transfer::transfer` inside of `a::my_module`, while `B` +can be transferred anywhere using `sui::transfer::public_transfer`. These rules are enforced by a +custom type system (bytecode verifier) rule in Sui. diff --git a/reference/src/abort-and-assert.md b/reference/src/abort-and-assert.md new file mode 100644 index 00000000..3fb0fbae --- /dev/null +++ b/reference/src/abort-and-assert.md @@ -0,0 +1,203 @@ +# Abort and Assert + +[`return`](./functions.md) and `abort` are two control flow constructs that end execution, one for +the current function and one for the entire transaction. + +More information on [`return` can be found in the linked section](./functions.md#return-expression) + +## `abort` + +`abort` is an expression that takes one argument: an **abort code** of type `u64`. For example: + +```move +abort 42 +``` + +The `abort` expression halts execution the current function and reverts all changes made to state by +the current transaction (note though that this guarantee must be upheld by the adapter of the +specific deployment of Move). There is no mechanism for "catching" or otherwise handling an `abort`. + +Luckily, in Move transactions are all or nothing, meaning any changes to storage are made all at +once only if the transaction succeeds. For Sui, this means no objects are modified. + +Because of this transactional commitment of changes, after an abort there is no need to worry about +backing out changes. While this approach is lacking in flexibility, it is incredibly simple and +predictable. + +Similar to [`return`](./functions.md), `abort` is useful for exiting control flow when some +condition cannot be met. + +In this example, the function will pop two items off of the vector, but will abort early if the +vector does not have two items + +```move= +use std::vector; +fun pop_twice(v: &mut vector): (T, T) { + if (vector::length(v) < 2) abort 42; + (vector::pop_back(v), vector::pop_back(v)) +} +``` + +This is even more useful deep inside a control-flow construct. For example, this function checks +that all numbers in the vector are less than the specified `bound`. And aborts otherwise + +```move= +use std::vector; +fun check_vec(v: &vector, bound: u64) { + let i = 0; + let n = vector::length(v); + while (i < n) { + let cur = *vector::borrow(v, i); + if (cur > bound) abort 42; + i = i + 1; + } +} +``` + +### `assert` + +`assert` is a builtin, macro operation provided by the Move compiler. It takes two arguments, a +condition of type `bool` and a code of type `u64` + +```move +assert!(condition: bool, code: u64) +``` + +Since the operation is a macro, it must be invoked with the `!`. This is to convey that the +arguments to `assert` are call-by-expression. In other words, `assert` is not a normal function and +does not exist at the bytecode level. It is replaced inside the compiler with + +```move +if (condition) () else abort code +``` + +`assert` is more commonly used than just `abort` by itself. The `abort` examples above can be +rewritten using `assert` + +```move= +use std::vector; +fun pop_twice(v: &mut vector): (T, T) { + assert!(vector::length(v) >= 2, 42); // Now uses 'assert' + (vector::pop_back(v), vector::pop_back(v)) +} +``` + +and + +```move= +use std::vector; +fun check_vec(v: &vector, bound: u64) { + let i = 0; + let n = vector::length(v); + while (i < n) { + let cur = *vector::borrow(v, i); + assert!(cur <= bound, 42); // Now uses 'assert' + i = i + 1; + } +} +``` + +Note that because the operation is replaced with this `if-else`, the argument for the `code` is not +always evaluated. For example: + +```move +assert!(true, 1 / 0) +``` + +Will not result in an arithmetic error, it is equivalent to + +```move +if (true) () else (1 / 0) +``` + +So the arithmetic expression is never evaluated! + +### Abort codes in the Move VM + +When using `abort`, it is important to understand how the `u64` code will be used by the VM. + +Normally, after successful execution, the Move VM, and the adapter for the specific deployment, +determine the changes made to storage. + +If an `abort` is reached, the VM will instead indicate an error. Included in that error will be two +pieces of information: + +- The module that produced the abort (package/address value and module name) +- The abort code. + +For example + +```move= +module 0x2::example { + public fun aborts() { + abort 42 + } +} + +module 0x3::invoker { + public fun always_aborts() { + 0x2::example::aborts() + } +} +``` + +If a transaction, such as the function `always_aborts` above, calls `0x2::example::aborts`, the VM +would produce an error that indicated the module `0x2::example` and the code `42`. + +This can be useful for having multiple aborts being grouped together inside a module. + +In this example, the module has two separate error codes used in multiple functions + +```move= +module 0x42::example { + + use std::vector; + + const EEmptyVector: u64 = 0; + const EIndexOutOfBounds: u64 = 1; + + // move i to j, move j to k, move k to i + public fun rotate_three(v: &mut vector, i: u64, j: u64, k: u64) { + let n = vector::length(v); + assert!(n > 0, EEmptyVector); + assert!(i < n, EIndexOutOfBounds); + assert!(j < n, EIndexOutOfBounds); + assert!(k < n, EIndexOutOfBounds); + + vector::swap(v, i, k); + vector::swap(v, j, k); + } + + public fun remove_twice(v: &mut vector, i: u64, j: u64): (T, T) { + let n = vector::length(v); + assert!(n > 0, EEmptyVector); + assert!(i < n, EIndexOutOfBounds); + assert!(j < n, EIndexOutOfBounds); + assert!(i > j, EIndexOutOfBounds); + + (vector::remove(v, i), vector::remove(v, j)) + } +} +``` + +## The type of `abort` + +The `abort i` expression can have any type! This is because both constructs break from the normal +control flow, so they never need to evaluate to the value of that type. + +The following are not useful, but they will type check + +```move +let y: address = abort 0; +``` + +This behavior can be helpful in situations where you have a branching instruction that produces a +value on some branches, but not all. For example: + +```move +let b = + if (x == 0) false + else if (x == 1) true + else abort 42; +// ^^^^^^^^ `abort 42` has type `bool` +``` diff --git a/reference/src/coding-conventions.md b/reference/src/coding-conventions.md new file mode 100644 index 00000000..0abe4ed7 --- /dev/null +++ b/reference/src/coding-conventions.md @@ -0,0 +1 @@ +See [Sui's Coding Conventions for Move](https://docs.sui.io/concepts/sui-move-concepts/conventions) diff --git a/reference/src/constants.md b/reference/src/constants.md new file mode 100644 index 00000000..4bf71e46 --- /dev/null +++ b/reference/src/constants.md @@ -0,0 +1,110 @@ +# Constants + +Constants are a way of giving a name to shared, static values inside of a `module`. + +The constant's value must be known at compilation. The constant's value is stored in the compiled +module. And each time the constant is used, a new copy of that value is made. + +## Declaration + +Constant declarations begin with the `const` keyword, followed by a name, a type, and a value. + +```text +const : = ; +``` + +For example + +```move +module a::example { + const MY_ADDRESS: address = @a; + + public fun permissioned(addr: address) { + assert!(addr == MY_ADDRESS, 0); + } +} +``` + +## Naming + +Constants must start with a capital letter `A` to `Z`. After the first letter, constant names can +contain underscores `_`, letters `a` to `z`, letters `A` to `Z`, or digits `0` to `9`. + +```move +const FLAG: bool = false; +const EMyErrorCode: u64 = 0; +const ADDRESS_42: address = @0x42; +``` + +Even though you can use letters `a` to `z` in a constant. The +[general style guidelines](./coding-conventions.md) are to use just uppercase letters `A` to `Z`, +with underscores `_` between each word. For error codes, we use `E` as a prefix and then upper camel +case (also known as Pascal case) for the rest of the name, as seen in `EMyErrorCode`. + +The current naming restriction of starting with `A` to `Z` is in place to give room for future +language features. + +## Visibility + +`public` or `public(package)` constants are not currently supported. `const` values can be used only +in the declaring module. However, as a convenience, they can be used across modules in +[unit tests attributes](./unit-testing.md). + +## Valid Expressions + +Currently, constants are limited to the primitive types `bool`, `u8`, `u16`, `u32`, `u64`, `u128`, +`u256`, `address`, and `vector`, where `T` is the valid type for a constant. + +### Values + +Commonly, `const`s are assigned a simple value, or literal, of their type. For example + +```move +const MY_BOOL: bool = false; +const MY_ADDRESS: address = @0x70DD; +const BYTES: vector = b"hello world"; +const HEX_BYTES: vector = x"DEADBEEF"; +``` + +### Complex Expressions + +In addition to literals, constants can include more complex expressions, as long as the compiler is +able to reduce the expression to a value at compile time. + +Currently, equality operations, all boolean operations, all bitwise operations, and all arithmetic +operations can be used. + +```move +const RULE: bool = true && false; +const CAP: u64 = 10 * 100 + 1; +const SHIFTY: u8 = { + (1 << 1) * (1 << 2) * (1 << 3) * (1 << 4) +}; +const HALF_MAX: u128 = 340282366920938463463374607431768211455 / 2; +const REM: u256 = + 57896044618658097711785492504343953926634992332820282019728792003956564819968 % 654321; +const EQUAL: bool = 1 == 1; +``` + +If the operation would result in a runtime exception, the compiler will give an error that it is +unable to generate the constant's value + +```move +const DIV_BY_ZERO: u64 = 1 / 0; // ERROR! +const SHIFT_BY_A_LOT: u64 = 1 << 100; // ERROR! +const NEGATIVE_U64: u64 = 0 - 1; // ERROR! +``` + +Additionally, constants can refer to other constants within the same module. + +```move +const BASE: u8 = 4; +const SQUARE: u8 = BASE * BASE; +``` + +Note though, that any cycle in the constant definitions results in an error. + +```move +const A: u16 = B + 1; +const B: u16 = A + 1; // ERROR! +``` diff --git a/reference/src/control-flow.md b/reference/src/control-flow.md new file mode 100644 index 00000000..2bfe3573 --- /dev/null +++ b/reference/src/control-flow.md @@ -0,0 +1,10 @@ +# Control Flow + +Move offers multiple constructs for control flow based on +[boolean expressions](./primitive-types/bool.md), including common programming constructs such as +`if` expressions and `while` and `for` loops, along with advanced control flow structures including +labels for loops and escapable named blocks. + +- [Conditional Expressions](./control-flow/conditionals.md) +- [Loops](./control-flow/loops.md) +- [Labeled Control FLow](./control-flow/labeled-control-flow.md) diff --git a/reference/src/control-flow/conditionals.md b/reference/src/control-flow/conditionals.md new file mode 100644 index 00000000..2045dd61 --- /dev/null +++ b/reference/src/control-flow/conditionals.md @@ -0,0 +1,66 @@ +# Conditional `if` Expressions + +An `if` expression specifies that some code should only be evaluated if a certain condition is true. +For example: + +```move +if (x > 5) x = x - 5 +``` + +The condition must be an expression of type `bool`. + +An `if` expression can optionally include an `else` clause to specify another expression to evaluate +when the condition is false. + +```move +if (y <= 10) y = y + 1 else y = 10 +``` + +Either the "true" branch or the "false" branch will be evaluated, but not both. Either branch can be +a single expression or an expression block. + +The conditional expressions may produce values so that the `if` expression has a result. + +```move +let z = if (x < 100) x else 100; +``` + +The expressions in the true and false branches must have compatible types. For example: + +```move= +// x and y must be u64 integers +let maximum: u64 = if (x > y) x else y; + +// ERROR! branches different types +let z = if (maximum < 10) 10u8 else 100u64; + +// ERROR! branches different types, as default false-branch is () not u64 +if (maximum >= 10) maximum; +``` + +If the `else` clause is not specified, the false branch defaults to the unit value. The following +are equivalent: + +```move +if (condition) true_branch // implied default: else () +if (condition) true_branch else () +``` + +Commonly, `if` expressions are used in conjunction with +[expression blocks](../variables.md#expression-blocks). + +```move +let maximum = if (x > y) x else y; +if (maximum < 10) { + x = x + 10; + y = y + 10; +} else if (x >= 10 && y >= 10) { + x = x - 10; + y = y - 10; +} +``` + +## Grammar for Conditionals + +> _if-expression_ → **if (** _expression_ **)** _expression_ _else-clause__opt_ > +> _else-clause_ → **else** _expression_ diff --git a/reference/src/control-flow/labeled-control-flow.md b/reference/src/control-flow/labeled-control-flow.md new file mode 100644 index 00000000..8f0c6a41 --- /dev/null +++ b/reference/src/control-flow/labeled-control-flow.md @@ -0,0 +1,65 @@ +# Labeled Control Flow + +Move supports labeled control flow, allowing you to define and transfer control to specific labels +in a function. For example, we can nest two loops and use `break` and `continue` with those labels +to precisely specify control flow. You can prefix any `loop` or `while` form with a `'label:` form +to allow breaking or continuing directly there. + +To demonstrate this behavior, consider a function that takes nested vectors of numbers (i.e., +`vector>`) to sum against some threshold, which behaves as follows: + +- If the sum of all the numbers are under the threshold, return that sum. +- If adding a number to the current sum would surpass the threshold, return the current sum. + +We can write this by iterating over the vector of vectors as nested loops and labelling the outer +one. If any addition in the inner loop would push us over the threshold, we can use `break` with the +outer label to escape both loops at once: + +```move +fun sum_until_threshold(input: &vector>, threshold: u64): u64 { + let mut sum = 0; + let mut i = 0; + let input_size = vector::length(vec); + + 'outer: loop { + // breaks to outer since it is the closest enclosing loop + if (i >= input_size) break sum; + + let vec = vector::borrow(input, i); + let size = vector::length(vec); + let mut j = 0; + + while (j < size) { + let v_entry = *vector::borrow(vec, j); + if (sum + v_entry < threshold) { + sum = sum + v_entry; + } else { + // the next element we saw would break the threshold, + // so we return the current sum + break 'outer sum + } + j = j + 1; + } + i = i + 1; + } +} +``` + +These sorts of labels can also be used with a nested loop form, providing precise control in larger +bodies of code. For example, if we were processing a large table where each entry required iteration +that might see us continuing the inner or outer loop, we could express that code using labels: + +```move +'outer: loop { + ... + 'inner: while (cond) { + ... + if (cond0) { break 'outer value } + ... + if (cond1) { continue 'inner } + else if (cond2) { continue 'outer } + ... + } + ... +} +``` diff --git a/reference/src/control-flow/loops.md b/reference/src/control-flow/loops.md new file mode 100644 index 00000000..2103cf5b --- /dev/null +++ b/reference/src/control-flow/loops.md @@ -0,0 +1,223 @@ +# Loop Constructs in Move + +Many programs require iteration over values, and Move provides `while` and `loop` forms to allow you +to write code in these situations. In addition, you can also modify control flow of these loops +during execution by using `break` (to exit the loop) and `continue` (to skip the remainder of this +iteration and return to the top of the control flow structure). + +## `while` Loops + +The `while` construct repeats the body (an expression of type unit) until the condition (an +expression of type `bool`) evaluates to `false`. + +Here is an example of simple `while` loop that computes the sum of the numbers from `1` to `n`: + +```move +fun sum(n: u64): u64 { + let mut sum = 0; + let mut i = 1; + while (i <= n) { + sum = sum + i; + i = i + 1 + }; + + sum +} +``` + +Infinite `while` loops are also allowed: + +```move= +fun foo() { + while (true) { } +} +``` + +### Using `break` Inside of `while` Loops + +In Move, `while` loops can use `break` to exit early. For example, suppose we were looking for the +position of a value in a vector, and would like to `break` if we find it: + +```move +fun find_position(values: &vector, target_value: u64): Option { + let size = vector::length(values); + let mut i = 0; + let mut found = false; + + while (i < size) { + if (vector::borrow(values, i) == &target_value) { + found = true; + break + }; + i = i + 1 + }; + + if (found) { + Option::Some(i) + } else { + Option::None + } +} +``` + +Here, if the borrowed vector value is equal to our target value, we set the `found` flag to `true` +and then call `break`, which will cause the program to exit the loop. + +Finally, note that `break` for `while` loops cannot take a value: `while` loops always return the +unit type `()` and thus `break` does, too. + +### Using `continue` Inside of `while` Loops + +Similar to `break`, Move's `while` loops can invoke `continue` to skip over part of the loop body. +This allows us to skip part of a computation if a condition is not met, such as in the following +example: + +```move +fun sum_even(values: &vector): u64 { + let size = vector::length(values); + let mut i = 0; + let mut even_sum = 0; + + while (i < size) { + let number = *vector::borrow(values, i); + i = i + 1; + if (number % 2 == 1) continue; + even_sum = even_sum + number; + }; + even_sum +} +``` + +This code will iterate over the provided vector. For each entry, if that entry is an even number, it +will add it to the `even_sum`. If it is not, however, it will call `continue`, skipping the sum +operation and returning to the `while` loop conditional check. + +## `loop` Expressions + +The `loop` expression repeats the loop body (an expression with type `()`) until it hits a `break`: + +```move +fun sum(n: u64): u64 { + let mut sum = 0; + let mut i = 1; + + loop { + i = i + 1; + if (i >= n) break; + sum = sum + i; + }; + + sum +} +``` + +Without a `break`, the loop will continue forever. In the example below, the program will run +forever because the `loop` does not have a `break`: + +```move +fun foo() { + let mut i = 0; + loop { i = i + 1 } +} +``` + +Here is an example that uses `loop` to write the `sum` function: + +```move +fun sum(n: u64): u64 { + let sum = 0; + let i = 0; + loop { + i = i + 1; + if (i > n) break; + sum = sum + i + }; + + sum +} +``` + +### Using `break` with Values in `loop` + +Unlike `while` loops, which always return `()`, a `loop` may return a value using `break`. In doing +so, the overall `loop` expression evaluates to a value of that type. For example, we can rewrite +`find_position` from above using `loop` and `break`, immediately returning the index if we find it: + +```move +fun find_position(values: &vector, target_value: u64): Option { + let size = vector::length(values); + let mut i = 0; + + loop { + if (vector::borrow(values, i) == &target_value) { + break Option::Some(i) + } else if (i >= size) { + break Option::None + }; + i = i + 1; + } +} +``` + +This loop will break with an option result, and, as the last expression in the function body, will +produce that value as the final function result. + +### Using `continue` Inside of `loop` Expressions + +As you might expect, `continue` can also be used inside a `loop`. Here is the previous `sum_even` +function rewritten using `loop` with `break `and` continue` instead of `while`. + +```move +fun sum_even(values: &vector): u64 { + let size = vector::length(values); + let mut i = 0; + let mut even_sum = 0; + + loop { + if (i >= size) break; + let number = *vector::borrow(values, i); + i = i + 1; + if (number % 2 == 1) continue; + even_sum = even_sum + number; + }; + even_sum +} +``` + +## The Type of `while` and `loop` + +In Move, loops are typed expressions. A `while` expression always has type `()`. + +```move +let () = while (i < 10) { i = i + 1 }; +``` + +If a `loop` contains a `break`, the expression has the type of the break. A break with no value has +the unit type `()`. + +```move +(loop { if (i < 10) i = i + 1 else break }: ()); +let () = loop { if (i < 10) i = i + 1 else break }; + +let x: u64 = loop { if (i < 10) i = i + 1 else break 5 }; +let x: u64 = loop { if (i < 10) { i = i + 1; continue} else break 5 }; +``` + +In addition, if a loop contains multiple breaks, they must all return the same type: + +```move +// invalid -- first break returns (), second returns 5 +let x: u64 = loop { if (i < 10) break else break 5 }; +``` + +If `loop` does not have a `break`, `loop` can have any type much like `return`, `abort`, `break`, +and `continue`. + +```move +(loop (): u64); +(loop (): address); +(loop (): &vector>); +``` + +If you need even more-precise control flow, such as breaking out of nested loops, the next chapter +presents the use of labeled control flow in Move. diff --git a/reference/src/equality.md b/reference/src/equality.md new file mode 100644 index 00000000..5c3b6083 --- /dev/null +++ b/reference/src/equality.md @@ -0,0 +1,174 @@ +# Equality + +Move supports two equality operations `==` and `!=` + +## Operations + +| Syntax | Operation | Description | +| ------ | --------- | --------------------------------------------------------------------------- | +| `==` | equal | Returns `true` if the two operands have the same value, `false` otherwise | +| `!=` | not equal | Returns `true` if the two operands have different values, `false` otherwise | + +### Typing + +Both the equal (`==`) and not-equal (`!=`) operations only work if both operands are the same type + +```move +0 == 0; // `true` +1u128 == 2u128; // `false` +b"hello" != x"00"; // `true` +``` + +Equality and non-equality also work over _all_ user defined types! + +```move= +module 0x42::example { + public struct S has copy, drop { f: u64, s: vector } + + fun always_true(): bool { + let s = S { f: 0, s: b"" }; + s == s + } + + fun always_false(): bool { + let s = S { f: 0, s: b"" }; + s != s + } +} +``` + +If the operands have different types, there is a type checking error + +```move +1u8 == 1u128; // ERROR! +// ^^^^^ expected an argument of type 'u8' +b"" != 0; // ERROR! +// ^ expected an argument of type 'vector' +``` + +### Typing with references + +When comparing [references](./primitive-types/references.md), the type of the reference (immutable +or mutable) does not matter. This means that you can compare an immutable `&` reference with a +mutable one `&mut` of the same underlying type. + +```move +let i = &0; +let m = &mut 1; + +i == m; // `false` +m == i; // `false` +m == m; // `true` +i == i; // `true` +``` + +The above is equivalent to applying an explicit freeze to each mutable reference where needed + +```move +let i = &0; +let m = &mut 1; + +i == freeze(m); // `false` +freeze(m) == i; // `false` +m == m; // `true` +i == i; // `true` +``` + +But again, the underlying type must be the same type + +```move +let i = &0; +let s = &b""; + +i == s; // ERROR! +// ^ expected an argument of type '&u64' +``` + +### Automatic Borrowing + +Starting in Move 2024 edition, the `==` and `!=` operators automatically borrow their operands if +one of the operands is a reference and the other is not. This means that the following code works +without any errors: + +```move +let r = &0; + +// In all cases, `0` is automatically borrowed as `&0` +r == 0; // `true` +0 == r; // `true` +r != 0; // `false` +0 != r; // `false` +``` + +This automatic borrow is always an immutable borrow. + +## Restrictions + +Both `==` and `!=` consume the value when comparing them. As a result, the type system enforces that +the type must have [`drop`](./abilities.md). Recall that without the +[`drop` ability](./abilities.md), ownership must be transferred by the end of the function, and such +values can only be explicitly destroyed within their declaring module. If these were used directly +with either equality `==` or non-equality `!=`, the value would be destroyed which would break +[`drop` ability](./abilities.md) safety guarantees! + +```move= +module 0x42::example { + public struct Coin has store { value: u64 } + fun invalid(c1: Coin, c2: Coin) { + c1 == c2 // ERROR! +// ^^ ^^ These assets would be destroyed! + } +} +``` + +But, a programmer can _always_ borrow the value first instead of directly comparing the value, and +reference types have the [`drop` ability](./abilities.md). For example + +```move= +module 0x42::example { + public struct Coin as store { value: u64 } + fun swap_if_equal(c1: Coin, c2: Coin): (Coin, Coin) { + let are_equal = &c1 == c2; // valid, note `c2` is automatically borrowed + if (are_equal) (c2, c1) else (c1, c2) + } +} +``` + +## Avoid Extra Copies + +While a programmer _can_ compare any value whose type has [`drop`](./abilities.md), a programmer +should often compare by reference to avoid expensive copies. + +```move= +let v1: vector = function_that_returns_vector(); +let v2: vector = function_that_returns_vector(); +assert!(copy v1 == copy v2, 42); +// ^^^^ ^^^^ +use_two_vectors(v1, v2); + +let s1: Foo = function_that_returns_large_struct(); +let s2: Foo = function_that_returns_large_struct(); +assert!(copy s1 == copy s2, 42); +// ^^^^ ^^^^ +use_two_foos(s1, s2); +``` + +This code is perfectly acceptable (assuming `Foo` has [`drop`](./abilities.md)), just not efficient. +The highlighted copies can be removed and replaced with borrows + +```move= +let v1: vector = function_that_returns_vector(); +let v2: vector = function_that_returns_vector(); +assert!(&v1 == &v2, 42); +// ^ ^ +use_two_vectors(v1, v2); + +let s1: Foo = function_that_returns_large_struct(); +let s2: Foo = function_that_returns_large_struct(); +assert!(&s1 == &s2, 42); +// ^ ^ +use_two_foos(s1, s2); +``` + +The efficiency of the `==` itself remains the same, but the `copy`s are removed and thus the program +is more efficient. diff --git a/reference/src/friends.md b/reference/src/friends.md new file mode 100644 index 00000000..3833a253 --- /dev/null +++ b/reference/src/friends.md @@ -0,0 +1,119 @@ +# DEPRECATED: Friends + +NOTE: this feature has been superceded by [`public(package)`](./functions.md#visibility). + +The `friend` syntax was used to declare modules that are trusted by the current module. A trusted +module is allowed to call any function defined in the current module that have the `public(friend)` +visibility. For details on function visibilities, refer to the _Visibility_ section in +[Functions](./functions.md). + +## Friend declaration + +A module can declare other modules as friends via friend declaration statements, in the format of + +- `friend ` — friend declaration using fully qualified module name like the example + below, or + + ```move + module 0x42::a { + friend 0x42::b; + } + ``` + +- `friend ` — friend declaration using a module name alias, where the module + alias is introduced via the `use` statement. + + ```move + module 0x42::a { + use 0x42::b; + friend b; + } + ``` + +A module may have multiple friend declarations, and the union of all the friend modules forms the +friend list. In the example below, both `0x42::B` and `0x42::C` are considered as friends of +`0x42::A`. + +```move +module 0x42::a { + + friend 0x42::b; + friend 0x42::c; +} +``` + +Unlike `use` statements, `friend` can only be declared in the module scope and not in the expression +block scope. `friend` declarations may be located anywhere a top-level construct (e.g., `use`, +`function`, `struct`, etc.) is allowed. However, for readability, it is advised to place friend +declarations near the beginning of the module definition. + +### Friend declaration rules + +Friend declarations are subject to the following rules: + +- A module cannot declare itself as a friend. + + ```move= + module 0x42::m { friend Self; // ERROR! } + // ^^^^ Cannot declare the module itself as a friend + + module 0x43::m { friend 0x43::M; // ERROR! } + // ^^^^^^^ Cannot declare the module itself as a friend + ``` + +- Friend modules must be known by the compiler + + ```move= + module 0x42::m { friend 0x42::nonexistent; // ERROR! } + // ^^^^^^^^^^^^^^^^^ Unbound module '0x42::nonexistent' + ``` + +- Friend modules must be within the same account address. + + ```move= + module 0x42::m {} + + module 0x42::n { friend 0x42::m; // ERROR! } + // ^^^^^^^ Cannot declare modules out of the current address as a friend + ``` + +- Friends relationships cannot create cyclic module dependencies. + + Cycles are not allowed in the friend relationships, e.g., the relation `0x2::a` friends `0x2::b` + friends `0x2::c` friends `0x2::a` is not allowed. More generally, declaring a friend module adds a + dependency upon the current module to the friend module (because the purpose is for the friend to + call functions in the current module). If that friend module is already used, either directly or + transitively, a cycle of dependencies would be created. + + ```move= + module 0x2::a { + use 0x2::c; + friend 0x2::b; + + public fun a() { + c::c() + } + } + + module 0x2::b { + friend 0x2::c; // ERROR! + // ^^^^^^ This friend relationship creates a dependency cycle: '0x2::b' is a friend of '0x2::a' uses '0x2::c' is a friend of '0x2::b' + } + + module 0x2::c { + public fun c() {} + } + ``` + +- The friend list for a module cannot contain duplicates. + + ```move= + module 0x42::a {} + + module 0x42::m { + use 0x42::a as aliased_a; + friend 0x42::A; + friend aliased_a; // ERROR! + // ^^^^^^^^^ Duplicate friend declaration '0x42::a'. Friend declarations in a module must be unique + } + ``` diff --git a/reference/src/functions.md b/reference/src/functions.md new file mode 100644 index 00000000..4aa65cc9 --- /dev/null +++ b/reference/src/functions.md @@ -0,0 +1,378 @@ +# Functions + +Functions are declared inside of modules and define the logic and behavior of the module. Functions +can be reused, either being called from other functions or as entry points for execution. + +## Declaration + +Functions are declared with the `fun` keyword followed by the function name, type parameters, +parameters, a return type, and finally the function body. + +```text +? ? fun <[type_parameters: constraint],*>([identifier: type],*): +``` + +For example + +```move +fun foo(x: u64, y: T1, z: T2): (T2, T1, u64) { (z, y, x) } +``` + +### Visibility + +Module functions, by default, can only be called within the same module. These internal (sometimes +called private) functions cannot be called from other modules or as entry points. + +```move +module a::m { + fun foo(): u64 { 0 } + fun calls_foo(): u64 { foo() } // valid +} + +module b::other { + fun calls_m_foo(): u64 { + a::m::foo() // ERROR! +// ^^^^^^^^^^^ 'foo' is internal to 'a::m' + } +} +``` + +To allow access from other modules, the function must be declared `public` or `public(package)`. +Tangential to visibility, an [`entry`](#entry-modifier) function can be called as an entry point for +execution. + +#### `public` visibility + +A `public` function can be called by _any_ function defined in _any_ module. As shown in the +following example, a `public` function can be called by: + +- other functions defined in the same module, +- functions defined in another module, or +- as an entry point for execution. + +```move +module a::m { + public fun foo(): u64 { 0 } + fun calls_foo(): u64 { foo() } // valid +} + +module b::other { + fun calls_m_foo(): u64 { + a::m::foo() // valid + } +} +``` + +Fore more details on the entry point to execution see [the section below](#entry-modifier). + +#### `public(package)` visibility + +The `public(package)` visibility modifier is a more restricted form of the `public` modifier to give +more control about where a function can be used. A `public(package)` function can be called by: + +- other functions defined in the same module, or +- other functions defined in the same package (the same address) + +```move +module a::m { + public(package) fun foo(): u64 { 0 } + fun calls_foo(): u64 { foo() } // valid +} + +module a::n { + fun calls_m_foo(): u64 { + a::m::foo() // valid, also in `a` + } +} + +module b::other { + fun calls_m_foo(): u64 { + b::m::foo() // ERROR! +// ^^^^^^^^^^^ 'foo' can only be called from a module in `a` + } +} +``` + +#### DEPRECATED `public(friend)` visibility + +Before the addition of `public(package)`, `public(friend)` was used to allow limited public access +to functions in the same package, but where the list of allowed modules had to be explicitly +enumerated by the callee's module. see [Friends](./friends.md) for more details. + +### `entry` modifier + +In addition to `public` functions, you might have some functions in your modules that you want to +use as the entry point to execution. The `entry` modifier is designed to allow module functions to +initiate execution, without having to expose the functionality to other modules. + +Essentially, the combination of `pbulic` and `entry` functions define the "main" functions of a +module, and they specify where Move programs can start executing. + +Keep in mind though, an `entry` function _can_ still be called by other Move functions. So while +they _can_ serve as the start of a Move program, they aren't restricted to that case. + +For example: + +```move +module a::m { + entry fun foo(): u64 { 0 } + fun calls_foo(): u64 { foo() } // valid! +} + +module a::n { + fun calls_m_foo(): u64 { + a::m::foo() // ERROR! +// ^^^^^^^^^^^ 'foo' is internal to 'a::m' + } +} +``` + +`entry` functions may have restrictions on their parameters and return types. Although, these +restrictions are specific to each individual deployment of Move. + +[The documentation for `entry` functions on Sui can be found here.](https://docs.sui.io/concepts/sui-move-concepts/entry-functions). + +### Name + +Function names can start with letters `a` to `z`. After the first character, function names can +contain underscores `_`, letters `a` to `z`, letters `A` to `Z`, or digits `0` to `9`. + +```move +fun fOO() {} +fun bar_42() {} +fun bAZ_19() {} +``` + +### Type Parameters + +After the name, functions can have type parameters + +```move +fun id(x: T): T { x } +fun example(x: T1, y: T2): (T1, T1, T2) { (copy x, x, y) } +``` + +For more details, see [Move generics](./generics.md). + +### Parameters + +Functions parameters are declared with a local variable name followed by a type annotation + +```move +fun add(x: u64, y: u64): u64 { x + y } +``` + +We read this as `x` has type `u64` + +A function does not have to have any parameters at all. + +```move +fun useless() { } +``` + +This is very common for functions that create new or empty data structures + +```move +module a::example { + public struct Counter { count: u64 } + + fun new_counter(): Counter { + Counter { count: 0 } + } +} +``` + +### Return type + +After the parameters, a function specifies its return type. + +```move +fun zero(): u64 { 0 } +``` + +Here `: u64` indicates that the function's return type is `u64`. + +Using [tuples](./primitive-types/tuples.md), a function can return multiple values: + +```move +fun one_two_three(): (u64, u64, u64) { (0, 1, 2) } +``` + +If no return type is specified, the function has an implicit return type of unit `()`. These +functions are equivalent: + +```move +fun just_unit(): () { () } +fun just_unit() { () } +fun just_unit() { } +``` + +As mentioned in the [tuples section](./primitive-types/tuples.md), these tuple "values" do not exist +as runtime values. This means that a function that returns unit `()` does not return any value +during execution. + +### Function body + +A function's body is an expression block. The return value of the function is the last value in the +sequence + +```move +fun example(): u64 { + let x = 0; + x = x + 1; + x // returns 'x' +} +``` + +See [the section below for more information on returns](#returning-values) + +For more information on expression blocks, see [Move variables](./variables.md). + +### Native Functions + +Some functions do not have a body specified, and instead have the body provided by the VM. These +functions are marked `native`. + +Without modifying the VM source code, a programmer cannot add new native functions. Furthermore, it +is the intent that `native` functions are used for either standard library code or for functionality +needed for the given Move environment. + +Most `native` functions you will likely see are in standard library code, such as `vector` + +```move +module std::vector { + native public fun length(v: &vector): u64; + ... +} +``` + +## Calling + +When calling a function, the name can be specified either through an alias or fully qualified + +```move +module a::example { + public fun zero(): u64 { 0 } +} + +module b::other { + use a::example::{Self, zero}; + fun call_zero() { + // With the `use` above all of these calls are equivalent + a::example::zero(); + example::zero(); + zero(); + } +} +``` + +When calling a function, an argument must be given for every parameter. + +```move +module a::example { + public fun takes_none(): u64 { 0 } + public fun takes_one(x: u64): u64 { x } + public fun takes_two(x: u64, y: u64): u64 { x + y } + public fun takes_three(x: u64, y: u64, z: u64): u64 { x + y + z } +} + +module b::other { + fun call_all() { + a::example::takes_none(); + a::example::takes_one(0); + a::example::takes_two(0, 1); + a::example::takes_three(0, 1, 2); + } +} +``` + +Type arguments can be either specified or inferred. Both calls are equivalent. + +```move +module aexample { + public fun id(x: T): T { x } +} + +module b::other { + fun call_all() { + a::example::id(0); + a::example::id(0); + } +} +``` + +For more details, see [Move generics](./generics.md). + +## Returning values + +The result of a function, its "return value", is the final value of its function body. For example + +```move +fun add(x: u64, y: u64): u64 { + x + y +} +``` + +The return value here is the result of `x + y`. + +[As mentioned above](#function-body), the function's body is an [expression block](./variables.md). +The expression block can sequence various statements, and the final expression in the block will be +be the value of that block + +```move +fun double_and_add(x: u64, y: u64): u64 { + let double_x = x * 2; + let double_y = y * 2; + double_x + double_y +} +``` + +The return value here is the result of `double_x + double_y` + +### `return` expression + +A function implicitly returns the value that its body evaluates to. However, functions can also use +the explicit `return` expression: + +```move +fun f1(): u64 { return 0 } +fun f2(): u64 { 0 } +``` + +These two functions are equivalent. In this slightly more involved example, the function subtracts +two `u64` values, but returns early with `0` if the second value is too large: + +```move +fun safe_sub(x: u64, y: u64): u64 { + if (y > x) return 0; + x - y +} +``` + +Note that the body of this function could also have been written as `if (y > x) 0 else x - y`. + +However `return` really shines is in exiting deep within other control flow constructs. In this +example, the function iterates through a vector to find the index of a given value: + +```move +use std::vector; +use std::option::{Self, Option}; +fun index_of(v: &vector, target: &T): Option { + let i = 0; + let n = vector::length(v); + while (i < n) { + if (vector::borrow(v, i) == target) return option::some(i); + i = i + 1 + }; + + option::none() +} +``` + +Using `return` without an argument is shorthand for `return ()`. That is, the following two +functions are equivalent: + +```move +fun foo() { return } +fun foo() { return () } +``` diff --git a/reference/src/generics.md b/reference/src/generics.md new file mode 100644 index 00000000..2ebe98d4 --- /dev/null +++ b/reference/src/generics.md @@ -0,0 +1,530 @@ +# Generics + +Generics can be used to define functions and structs over different input data types. This language +feature is sometimes referred to as parametric polymorphism. In Move, we will often use the term +generics interchangeably with _type parameters_ and _type arguments_. + +Generics are commonly used in library code, such as in [vector](./primitive-types/vector.md), to +declare code that works over any possible type (that satisfies the specified constraints). This sort +of parameterization allows you to reuse the same implementation across multiple types and +situations. + +## Declaring Type Parameters + +Both functions and structs can take a list of type parameters in their signatures, enclosed by a +pair of angle brackets `<...>`. + +### Generic Functions + +Type parameters for functions are placed after the function name and before the (value) parameter +list. The following code defines a generic identity function that takes a value of any type and +returns that value unchanged. + +```move +fun id(x: T): T { + // this type annotation is unnecessary but valid + (x: T) +} +``` + +Once defined, the type parameter `T` can be used in parameter types, return types, and inside the +function body. + +### Generic Structs + +Type parameters for structs are placed after the struct name, and can be used to name the types of +the fields. + +```move +public struct Foo has copy, drop { x: T } + +public struct Bar has copy, drop { + x: T1, + y: vector, +} +``` + +Note that [type parameters do not have to be used](#unused-type-parameters) + +## Type Arguments + +### Calling Generic Functions + +When calling a generic function, one can specify the type arguments for the function's type +parameters in a list enclosed by a pair of angle brackets. + +```move +fun foo() { + let x = id(true); +} +``` + +If you do not specify the type arguments, Move's [type inference](#type-inference) will supply them +for you. + +### Using Generic Structs + +Similarly, one can attach a list of type arguments for the struct's type parameters when +constructing or destructing values of generic types. + +```move +fun foo() { + // type arguments on construction + let foo = Foo { x: true }; + let bar = Bar { x: 0, y: vector[] }; + + // type arguments on destruction + let Foo { x } = foo; + let Bar { x, y } = bar; +} +``` + +In any case if you do not specify the type arguments, Move's [type inference](#type-inference) will +supply them for you. + +### Type Argument Mismatch + +If you specify the type arguments and they conflict with the actual values supplied, an error will +be given: + +```move +fun foo() { + let x = id(true); // ERROR! true is not a u64 +} +``` + +and similarly: + +```move +fun foo() { + let foo = Foo { x: 0 }; // ERROR! 0 is not a bool + let Foo
{ x } = foo; // ERROR! bool is incompatible with address +} +``` + +## Type Inference + +In most cases, the Move compiler will be able to infer the type arguments so you don't have to write +them down explicitly. Here's what the examples above would look like if we omit the type arguments: + +```move +fun foo() { + let x = id(true); + // ^ is inferred + + let foo = Foo { x: true }; + // ^ is inferred + + let Foo { x } = foo; + // ^ is inferred +} +``` + +Note: when the compiler is unable to infer the types, you'll need annotate them manually. A common +scenario is to call a function with type parameters appearing only at return positions. + +```move +module a::m { + + fun foo() { + let v = vector[]; // ERROR! + // ^ The compiler cannot figure out the element type, since it is never used + + let v = vector[]; + // ^~~~~ Must annotate manually in this case. + } +} +``` + +Note that these cases are a bit contrived since the `vector[]` is never used, ad as such, Move's +type inference cannot infer the type. + +However, the compiler will be able to infer the type if that value is used later in that function: + +```move +module a::m { + fun foo() { + let v = vector[]; + // ^ is inferred + vector::push_back(&mut v, 42); + // ^ is inferred + } +} +``` + +## Integers + +In Move, the integer types `u8`, `u16`, `u32`, `u64`, `u128`, and `u256` are all distinct types. +However, each one of these types can be created with the same numerical value syntax. In other +words, if a type suffix is not provided, the compiler will infer the integer type based on the usage +of the value. + +```move +let x8: u8 = 0; +let x16: u16 = 0; +let x32: u32 = 0; +let x64: u64 = 0; +let x128: u128 = 0; +let x256: u256 = 0; +``` + +If the value is not used in a context that requires a specific integer type, `u64` is taken as a +default. + +```move +let x = 0; +// ^ u64 is used by default +``` + +If the value however is too large for the inferred type, an error will be given + +```move +let i: u8 = 256; // ERROR! +// ^^^ too large for u8 +let x = 340282366920938463463374607431768211454; +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ too large for u64 +``` + +In cases where the number is too large, you might need to annotate it explicitly + +```move +let x = 340282366920938463463374607431768211454u128; +// ^^^^ valid! +``` + +## Unused Type Parameters + +For a struct definition, an unused type parameter is one that does not appear in any field defined +in the struct, but is checked statically at compile time. Move allows unused type parameters so the +following struct definition is valid: + +```move +public struct Foo { + foo: u64 +} +``` + +This can be convenient when modeling certain concepts. Here is an example: + +```move +module a::m { + // Currency Specifiers + public struct A {} + public struct B {} + + // A generic coin type that can be instantiated using a currency + // specifier type. + // e.g. Coin, Coin etc. + public struct Coin has store { + value: u64 + } + + // Write code generically about all currencies + public fun mint_generic(value: u64): Coin { + Coin { value } + } + + // Write code concretely about one currency + public fun mint_a(value: u64): Coin { + mint_generic(value) + } + public fun mint_b(value: u64): Coin { + mint_generic(value) + } +} +``` + +In this example, `Coin` is generic on the `Currency` type parameter, which specifies the +currency of the coin and allows code to be written either generically on any currency or concretely +on a specific currency. This generality applies even when the `Currency` type parameter does not +appear in any of the fields defined in `Coin`. + +### Phantom Type Parameters + +In the example above, although `struct Coin` asks for the `store` ability, neither `Coin` nor +`Coin` will have the `store` ability. This is because of the rules for +[Conditional Abilities and Generic Types](./abilities.md#conditional-abilities-and-generic-types) +and the fact that `A` and `B` don't have the `store` ability, despite the fact that they are not +even used in the body of `struct Coin`. This might cause some unpleasant consequences. For example, +we are unable to put `Coin` into a wallet in storage. + +One possible solution would be to add spurious ability annotations to `A` and `B` (i.e., +`public struct Currency1 has store {}`). But, this might lead to bugs or security vulnerabilities +because it weakens the types with unnecessary ability declarations. For example, we would never +expect a value in the storage to have a field in type `A`, but this would be possible with the +spurious `store` ability. Moreover, the spurious annotations would be infectious, requiring many +functions generic on the unused type parameter to also include the necessary constraints. + +Phantom type parameters solve this problem. Unused type parameters can be marked as _phantom_ type +parameters, which do not participate in the ability derivation for structs. In this way, arguments +to phantom type parameters are not considered when deriving the abilities for generic types, thus +avoiding the need for spurious ability annotations. For this relaxed rule to be sound, Move's type +system guarantees that a parameter declared as `phantom` is either not used at all in the struct +definition, or it is only used as an argument to type parameters also declared as `phantom`. + +#### Declaration + +In a struct definition a type parameter can be declared as phantom by adding the `phantom` keyword +before its declaration. + +```move +public struct Coin has store { + value: u64 +} +``` + +If a type parameter is declared as phantom we say it is a phantom type parameter. When defining a +struct, Move's type checker ensures that every phantom type parameter is either not used inside the +struct definition or it is only used as an argument to a phantom type parameter. + +```move +public struct S1 { f: u64 } +// ^^^^^^^ valid, T1 does not appear inside the struct definition + +public struct S2 { f: S1 } +// ^^^^^^^ valid, T1 appears in phantom position +``` + +The following code shows examples of violations of the rule: + +```move +public struct S1 { f: T } +// ^^^^^^^ ERROR! ^ Not a phantom position + +public struct S2 { f: T } +public struct S3 { f: S2 } +// ^^^^^^^ ERROR! ^ Not a phantom position +``` + +More formally, if a type is used as an argument to a phantom type parameter we say the type appears +in _phantom position_. With this definition in place, the rule for the correct use of phantom +parameters can be specified as follows: **A phantom type parameter can only appear in phantom +position**. + +Note that specifying `phantom` is not required, but the compiler will warn if a type parameter could +be `phantom` but was not marked as such. + +#### Instantiation + +When instantiating a struct, the arguments to phantom parameters are excluded when deriving the +struct abilities. For example, consider the following code: + +```move +public struct S has copy { f: T1 } +public struct NoCopy {} +public struct HasCopy has copy {} +``` + +Consider now the type `S`. Since `S` is defined with `copy` and all non-phantom +arguments have `copy` then `S` also has `copy`. + +#### Phantom Type Parameters with Ability Constraints + +Ability constraints and phantom type parameters are orthogonal features in the sense that phantom +parameters can be declared with ability constraints. + +```move +public struct S {} +``` + +When instantiating a phantom type parameter with an ability constraint, the type argument has to +satisfy that constraint, even though the parameter is phantom. The usual restrictions apply and `T` +can only be instantiated with arguments having `copy`. + +## Constraints + +In the examples above, we have demonstrated how one can use type parameters to define "unknown" +types that can be plugged in by callers at a later time. This however means the type system has +little information about the type and has to perform checks in a very conservative way. In some +sense, the type system must assume the worst case scenario for an unconstrained generic--a type with +no [abilities](./abilities.md). + +Constraints offer a way to specify what properties these unknown types have so the type system can +allow operations that would otherwise be unsafe. + +### Declaring Constraints + +Constraints can be imposed on type parameters using the following syntax. + +```move +// T is the name of the type parameter +T: (+ )* +``` + +The `` can be any of the four [abilities](./abilities.md), and a type parameter can be +constrained with multiple abilities at once. So all of the following would be valid type parameter +declarations: + +```move +T: copy +T: copy + drop +T: copy + drop + store + key +``` + +### Verifying Constraints + +Constraints are checked at instantiation sites + +```move +public struct Foo { x: T } + +public struct Bar { x: Foo } +// ^^ valid, u8 has `copy` + +public struct Baz { x: Foo } +// ^ ERROR! T does not have 'copy' +``` + +And similarly for functions + +```move +fun unsafe_consume(x: T) { + // ERROR! x does not have 'drop' +} + +fun consume(x: T) { + // valid, x will be dropped automatically +} + +public struct NoAbilities {} + +fun foo() { + let r = NoAbilities {}; + consume(NoAbilities); + // ^^^^^^^^^^^ ERROR! NoAbilities does not have 'drop' +} +``` + +And some similar examples, but with `copy` + +```move +fun unsafe_double(x: T) { + (copy x, x) + // ERROR! T does not have 'copy' +} + +fun double(x: T) { + (copy x, x) // valid, T has 'copy' +} + +public struct NoAbilities {} + +fun foo(): (NoAbilities, NoAbilities) { + let r = NoAbilities {}; + double(r) + // ^ ERROR! NoAbilities does not have 'copy' +} +``` + +For more information, see the abilities section on +[conditional abilities and generic types](./abilities.md#conditional-abilities-and-generic-types). + +## Limitations on Recursions + +### Recursive Structs + +Generic structs can not contain fields of the same type, either directly or indirectly, even with +different type arguments. All of the following struct definitions are invalid: + +```move +public struct Foo { + x: Foo // ERROR! 'Foo' containing 'Foo' +} + +public struct Bar { + x: Bar // ERROR! 'Bar' containing 'Bar' +} + +// ERROR! 'A' and 'B' forming a cycle, which is not allowed either. +public struct A { + x: B +} + +public struct B { + x: A + y: A +} +``` + +### Advanced Topic: Type-level Recursions + +Move allows generic functions to be called recursively. However, when used in combination with +generic structs, this could create an infinite number of types in certain cases, and allowing this +means adding unnecessary complexity to the compiler, vm and other language components. Therefore, +such recursions are forbidden. + +This restriction might be relaxed in the future, but for now, the following examples should give you +an idea of what is allowed and what is not. + +```move +module a::m { + public struct A {} + + // Finitely many types -- allowed. + // foo -> foo -> foo -> ... is valid + fun foo() { + foo(); + } + + // Finitely many types -- allowed. + // foo -> foo> -> foo> -> ... is valid + fun foo() { + foo>(); + } +} +``` + +Not allowed: + +```move +module a::m { + public struct A {} + + // Infinitely many types -- NOT allowed. + // error! + // foo -> foo> -> foo>> -> ... + fun foo() { + foo>(); + } +} +``` + +And similarly, not allowed: + +```move +module a::n { + public struct A {} + + // Infinitely many types -- NOT allowed. + // error! + // foo -> bar -> foo> + // -> bar, T2> -> foo, A> + // -> bar, A> -> foo, A>> + // -> ... + fun foo() { + bar(); + } + + fun bar { + foo>(); + } +} +``` + +Note, the check for type level recursions is based on a conservative analysis on the call sites and +does NOT take control flow or runtime values into account. + +```move +module a::m { + public struct A {} + + // Infinitely many types -- NOT allowed. + // error! + fun foo(n: u64) { + if (n > 0) foo>(n - 1); + } +} +``` + +The function in the example above will technically terminate for any given input and therefore only +creating finitely many types, but it is still considered invalid by Move's type system. diff --git a/reference/src/index-syntax.md b/reference/src/index-syntax.md new file mode 100644 index 00000000..d4e57dc1 --- /dev/null +++ b/reference/src/index-syntax.md @@ -0,0 +1,298 @@ +# Index Syntax + +Move provides syntax attributes to allow you to define operations that look and feel like native +move code, lowering these operations into your user-provided definitions. + +Our first syntax method, `index`, allows you to define a group of operations that can be used as +custom index accessors for your datatypes, such as accessing a matrix element as `m[i,j]`, by +annotating functions that should be used for these index operations. Moreover, these definitions are +bespoke per-type and available implicitly for any programmer using your type. + +## Overview and Summary + +To start, consider a `Matrix` type that uses a vector of vectors to represent its values. You can +write a small library using `index` syntax annotations on the `borrow` and `borrow_mut` functions as +follows: + +``` +module matrix { + + public struct Matrix { v: vector> } + + #[syntax(index)] + public fun borrow(s: &Matrix, i: u64, j: u64): &T { + borrow(borrow(s.v, i), j) + } + + #[syntax(index)] + public fun borrow_mut(s: &mut Matrix, i: u64, j: u64): &mut T { + borrow_mut(borrow_mut(s.v, i), j) + } + + public fun make_matrix(v: vector>): Matrix { + Matrix { v } + } + +} +``` + +Now anyone using this `Matrix` type has access to index syntax for it: + +``` +let v0 = vector[1, 0, 0]; +let v1 = vector[0, 1, 0]; +let v2 = vector[0, 0, 1]; +let v = vector>[v0, v1, v2]; +let mut m = matrix::make_matrix(v); + +let mut i = 0; +while (i < 3) { + let mut j = 0; + while (j < 3) { + if (i == j) { + assert!(m[i, j] == 1, i); + } else { + assert!(m[i, j] == 0, i + 10); + }; + *(&mut m[i,j]) = 2; + j = j + 1; + }; + i = i + 1; +} +``` + +## Usage + +As the example indicates, if you define a datatype and an associated index syntax method, anyone can +invoke that method by writing index syntax on a value of that type: + +```move +let mat = matrix::make_matrix(...); +let m_0_0 = mat[0, 0]; +``` + +During compilation, the compiler translates these into the appropriate function invocations based on +the position and mutable usage of the expression: + +````move +let mut mat = matrix::make_matrix(...); + +let m_0_0 = mat[0, 0]; + // translates to copy matrix::borrow(&mat, 0, 0) + +let m_0_0 = &mat[0, 0]; + // translates to matrix::borrow(&mat, 0, 0) + +let m_0_0 = &mut mat[0, 0]; + // translates to matrix::borrow_mut(&mut mat, 0, 0) +`` + +You can also intermix index expressions with field accesses: + +```move +public struct V { v: vector } + +public struct Vs { vs: vector } + +fun borrow_first(input: &Vs): &u64 { + input.vs[0].v[0] + // translates to vector::borrow(vector::borrow(input.vs, 0).v, 0) +} +```` + +### Index Functions Take Flexible Arguments + +Note that, aside from the definition and type limitations described in the rest of this chapter, +Move places no restrictions on the values your index syntax method takes as parameters. This allows +you to implement intricate programmatic behavior when defining index syntax, such as a data +structure that takes a default value if the index is out of bounds: + +``` +#[syntax(index)] +public fun borrow_or_set( + input: &mut MTable, + key: &Key, + default: Value +): &mut Value { + if (contains(input, *key)) { + borrow(input, key) + } else { + insert(input, *key, default) + borrow(input, key) + } +} +``` + +Now, when you index into `MTable`, you must also provide a default value: + +``` +let string_key: String = ...; +let mut table: MTable = m_table::make_table(); +let entry: &mut u64 = &mut table[string_key, 0]; +``` + +This sort of extensible power allows you to write precise index interfaces for your types, +concretely enforcing bespoke behavior. + +## Defining Index Syntax Functions + +This powerful syntax form allows all of your user-defined datatypes to behave in this way, assuming +your definitions adhere to the following rules: + +1. The `#[syntax(index)]` attribute is added to the designated functions defined in the same module + as the subject type. +1. The designated functions have `public` visibility. +1. The functions take a reference type as its subject type (its first argument) and returns a + matching references type (`mut` if the subject was `mut`). +1. Each type has only a single mutable and single immutable definition. +1. Immutable and mutable versions have type agreement: + - The subject types match, differing only in mutability. + - The return types match the mutability of their subject types. + - Type parameters, if present, have identical constraints between both versions. + - All parameters beyond the subject type are identical. + +The following content and additional examples describe these rules in greater detail. + +### Declaration + +To declare an index syntax method, add the `#[syntax(index)]` attribute above the relevant function +definition in the same module as the subject type's definition. This signals to the compiler that +the function is an index accessor for the specified type. + +#### Immutable Accessor + +The immutable index syntax method is defined for read-only access. It takes an immutable reference +of the subject type and returns an immutable reference to the element type. The `borrow` function +defined in `std::vector` is an example of this: + +```move +#[syntax(index)] +public fun borrow(v: &vector, i: u64): &Element { + // implementation +} +``` + +#### Mutable Accessor + +The mutable index syntax method is the dual of the immutable one, allowing for both read and write +operations. It takes a mutable reference of the subject type and returns a mutable reference to the +element type. The `borrow_mut` function defined in `std::vector` is an example of this: + +```move +#[syntax(index)] +public fun borrow_mut(v: &mut vector, i: u64): &mut Element { + // implementation +} +``` + +#### Visibility + +To ensure that indexing functions are available anywhere the type is used, all index syntax methods +must have public visibility. This ensures ergonomic usage of indexing across modules and packages in +Move. + +#### No Duplicates + +In addition to the above requirements, we restrict each subject base type to defining a single index +syntax method for immutable references and a single index syntax method for mutable references. For +example, you cannot define a specialized version for a polymorphic type: + +```move +#[syntax(index)] +public fun borrow_matrix_u64(s: &Matrix, i: u64, j: u64): &u64 { ... } + +#[syntax(index)] +public fun borrow_matrix(s: &Matrix, i: u64, j: u64): &T { ... } + // ERROR! Matrix already has a definition + // for its immutable index syntax method +``` + +This ensures that you can always tell which method is being invoked, without the need to inspect +type instantiation. + +### Type Constraints + +By default, an index syntax method has the following type constraints: + +**Its subject type (first argument) must be a reference to a single type defined in the same module +as the marked function.** This means that you cannot define index syntax methods for tuples, type +parameters, or values: + +```move +#[syntax(index)] +public fun borrow_fst(x: &(u64, u64), ...): &u64 { ... } + // ERROR because the subject type is a tuple + +#[syntax(index)] +public fun borrow_tyarg(x: &T, ...): &T { ... } + // ERROR because the subject type is a type parameter + +#[syntax(index)] +public fun borrow_value(x: Matrix, ...): &u64 { ... } + // ERROR because x is not a reference +``` + +**The subject type must match mutability with the return type.** This restriction allows you to +clarify the expected behavior when borrowing an indexed expression as `&vec[i]` versus +`&mut vec[i]`. The Move compiler uses the mutability marker to determine which borrow form to call +to produce a reference of the appropriate mutability. As a result, we disallow index syntax methods +whose subject and return mutability differ: + +```move +#[syntax(index)] +public fun borrow_imm(x: &mut Matrix, ...): &u64 { ... } + // ERROR! incompatible mutability + +``` + +### Type Compatibility + +When defining an immutable and mutable index syntax method pair, they are subject to a number of +compatibility constraints: + +1. They must take the same number of type parameters, those type parameters must have the same + constraints. +1. Type parameters must be used the same _by position_, not name. +1. Their subject types must match exactly except for the mutability. +1. Their return types must match exactly except for the mutability. +1. All other parameter types must match exactly. + +These constraints are to ensure that index syntax behaves identically regardless of being in a +mutable or immutable position. + +To illustrate some of these errors, recall the previous `Matrix` definition: + +```move +#[syntax(index)] +public fun borrow(s: &Matrix, i: u64, j: u64): &T { + borrow(borrow(s.v, i), j) +} +``` + +All of the following are type-incompatible definitions of the mutable version: + +```move +#[syntax(index)] +public fun borrow_mut(s: &mut Matrix, i: u64, j: u64): &mut T { ... } + // ERROR! `T` has `drop` here, but no in the immutable version + +#[syntax(index)] +public fun borrow_mut(s: &mut Matrix, i: u64, j: u64): &mut u64 { ... } + // ERROR! This takes a different number of type parameters + +#[syntax(index)] +public fun borrow_mut(s: &mut Matrix, i: u64, j: u64): &mut U { ... } + // ERROR! This takes a different number of type parameters + +#[syntax(index)] +public fun borrow_mut(s: &mut Matrix, i_j: (u64, u64)): &mut U { ... } + // ERROR! This takes a different number of arguments + +#[syntax(index)] +public fun borrow_mut(s: &mut Matrix, i: u64, j: u32): &mut U { ... } + // ERROR! `j` is a different type +``` + +Again, the goal here is to make the usage across the immutable and mutable versions consistent. This +allows index syntax methods to work without changing out the behavior or constraints based on +mutable versus immutable usage, ultimately ensuring a consistent interface to program against. diff --git a/reference/src/introduction.md b/reference/src/introduction.md new file mode 100644 index 00000000..806b868f --- /dev/null +++ b/reference/src/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +Welcome to Move, a next generation language for secure asset programming. Its primary use case is in +blockchain environments, where Move programs are used to construct state changes. Move allows +developers to write programs that flexibly manage and transfer assets, while providing the security +and protections against attacks on those assets. However, Move has been developed with use cases in +mind outside a blockchain context as well. + +Move takes its cue from [Rust](https://www.rust-lang.org/) by using resource types with move (hence +the name) semantics as an explicit representation of digital assets, such as currency. + + diff --git a/reference/src/method-syntax.md b/reference/src/method-syntax.md new file mode 100644 index 00000000..bcd7124f --- /dev/null +++ b/reference/src/method-syntax.md @@ -0,0 +1,441 @@ +# Methods + +As a syntactic convience, some functions in Move can be called as "methods" on a value. This is done +by using the `.` operator to call the function, where the value on the left-hand side of the `.` is +the first argument to the function (sometimes called the receiver). The type of that value +statically determines which function is called. This is an important difference from some other +langauges, where this syntax might indicate a dynamic call, where the function to be called is +determined at runtime. In Move, all function calls are statically determined. + +In short, this syntax exists to make it easier to call functions without having to create an alias +with `use`, and without having to explicitly borrow the first argument to the function. +Additionally, this can make code more readable, as it reduces the amount of boilerplate needed to +call a function and makes it easier to chain function calls. + +## Syntax + +The syntax for calling a method is as follows: + +```text + . <[type_arguments],*> ( ) +``` + +For example + +```move +coin.value(); +*nums.borrow_mut(i) = 5; +``` + +## Method Resolution + +When a method is called, the compiler will statically determine which function is called based on +the type of the receiver (the argument on the left-hand side of the `.`). The compiler maintains a +mapping from type and method name to the module and function name that should be called. This +mapping is created fom the `use fun` aliases that are currently in scope, and from the appropriate +functions in the receiver type's defining module. In all cases, the receiver type is the first +argument to the function, whether by-value or by-reference. + +In this section, when we say a method "resolves" to a function, we mean that the compiler will +statically replace the method with a normal [function](./functions.md) call. For example if we have +`x.foo(e)` with `foo` resolving to `a::m::foo`, the compiler will replace `x.foo(e)` with +`a::m::foo(x, e)`, potentially [automatically borrowing](#automatic-borrowing) `x`. + +### Functions in the Defining Module + +In a type’s defining module, the compiler will automatically create a method alias for any function +declaration for its types when the type is the first argument in the function. For example, + +```move +module a::m { + public struct X() has copy, drop, store; + public fun foo(x: &X) { ... } + public fun bar(flag: bool, x: &X) { ... } +} +``` + +The function `foo` can be called as a method on a value of type `X`. However, not the first argument +(and one is not created for `bool` since `bool` is not defined in that module). For example, + +```move +fun example(x: a::m::X) { + x.foo(); // valid + // x.bar(true); ERROR! +} +``` + +### `use fun` Aliases + +Like a traditional [`use`](uses.md), a `use fun` statement creates an alias local to its current +scope. This could be for the current module or the current expression block. However, the alias is +associated to a type. + +The syntax for a `use fun` statement is as follows: + +```move +use fun as .; +``` + +This creates an alias for the ``, which the `` can receive as ``. + +For example + +```move +module a::cup { + public struct Cup(T) has copy, drop, store; + + public fun cup_borrow(c: &Cup): &T { + &c.0 + } + + public fun cup_value(c: Cup): T { + let Cup(t) = c; + t + } + + public fun cup_swap(c: &mut Cup, t: T) { + c.0 = t; + } +} +``` + +We can now create `use fun` aliases to these functions + +```move +module b::example { + use fun a::cup::cup_borrow as Cup.borrow; + use fun a::cup::cup_value as Cup.value; + use fun a::cup::cup_swap as Cup.set; + + fun example(c: &mut Cup) { + let _ = c.borrow(); // resolves to a::cup::cup_borrow + let v = c.value(); // resolves to a::cup::cup_value + c.set(v * 2); // resolves to a::cup::cup_swap + } +} +``` + +Note that the `` in the `use fun` does not have to be a fully resolved path, and an alias +can be used instead, so the declarations in the above example could equivalently be written as + +```move + use a::cup::{Self, cup_swap}; + + use fun cup::cup_borrow as Cup.borrow; + use fun cup::cup_value as Cup.value; + use fun cup_swap as Cup.set; +``` + +While these examples are cute for renaming the functions in the current module, the feature is +perhaps more useful for declaring methods on types from other modules. For example, if we wanted to +add a new utility to `Cup`, we could do so with a `use fun` alias and still use method syntax + +```move +module b::example { + + fun double(c: &Cup): Cup { + let v = c.value(); + Cup::new(v * 2) + } + +} +``` + +Normally, we would be stuck having to call it as `double(&c)` because `b::example` did not define +`Cup`, but instead we can use a `use fun` alias + +```move + fun double_double(c: Cup): (Cup, Cup) { + use fun b::example::double as Cup.dub; + (c.dub(), c.dub()) // resolves to b::example::double in both calls + } +``` + +While `use fun` can be made in any scope, the target `` of the `use fun` must have a first +argument that is the same as the ``. + +```move +public struct X() has copy, drop, store; + +fun new(): X { X() } +fun flag(flag: bool): u8 { if (flag) 1 else 0 } + +use fun new as X.new; // ERROR! +use fun flag as X.flag; // ERROR! +// Neither `new` nor `flag` has first argument of type `X` +``` + +But any first argument of the `` can be used, including references and mutable references + +```move +public struct X() has copy, drop, store; + +public fun by_val(_: X) {} +public fun by_ref(_: &X) {} +public fun by_mut(_: &mut X) {} + +// All 3 valid, in any scope +use fun by_val as X.v; +use fun by_ref as X.r; +use fun by_mut as X.m; +``` + +Note for generics, the methods are associated for _all_ instances of the generic type. You cannot +overload the method to resolve to different functions depending on the instantiation. + +```move +public struct Cup(T) has copy, drop, store; + +public fun value(c: &Cup): T { + c.0 +} + +use fun value as Cup.flag; // ERROR! +use fun value as Cup.num; // ERROR! +// In both cases, `use fun` aliases cannot be generic, they must work for all instances of the type +``` + +### `public use fun` Aliases + +Unlike a traditional [`use`](uses.md), the `use fun` statement can be made `public`, which allows it +to be used outside of its declared scope. A `use fun` can be made `public` if it is declared in the +module that defines the receivers type, much like the method aliases that are +[automatically created](#functions-in-the-defining-module) for functions in the defining module. Or +conversely, one can think that an implicit `public use fun` is created automatically for every +function in the defining module that has a first argument of the receiver type (if it is defined in +that module). Both of these views are equivalent. + +```move +module a::cup { + public struct Cup(T) has copy, drop, store; + + public use fun cup_borrow as Cup.borrow; + public fun cup_borrow(c: &Cup): &T { + &c.0 + } +} +``` + +In this example, a public method alias is created for `a::cup::Cup.borrow` and +`a::cup::Cup.cup_borrow`. Both resolve to `a::cup::cup_borrow`. And both are "public" in the sense +that they can be used outside of `a::cup`, without an additional `use` or `use fun`. + +```move +module b::example { + + fun example(c: a::cup::Cup) { + c.borrow(); // resolves to a::cup::cup_borrow + c.cup_borrow(); // resolves to a::cup::cup_borrow + } +} +``` + +The `public use fun` declarations thus serve as a way of renaming a function if you want to give it +a cleaner name for use with method syntax. This is especially helpful if you have a module with +multiple types, and similarly named functions for each type. + +```move +module a::shapes { + + public struct Rectangle { base: u64, height: u64 } + public struct Box { base: u64, height: u64, depth: u64 } + + // Rectangle and Box can have methods with the same name + + public use fun rectangle_base as Rectangle.base; + public fun rectangle_base(rectangle: &Rectangle): u64 { + rectangle.base + } + + public use fun box_base as Box.base; + public fun box_base(box: &Box): u64 { + box.base + } + +} +``` + +Another use for `public use fun` is adding methods to types from other modules. This can be helpful +in conjunction with functions spread out across a single package. + +```move +module a::cup { + public struct Cup(T) has copy, drop, store; + + public fun new(t: T): Cup { Cup(t) } + public fun borrow(c: &Cup): &T { + &c.0 + } + // `public use fun` to a function defined in another module + public use fun a::utils::split as Cup.split; +} + +module a::utils { + use a::m::{Self, Cup}; + + public fun split(c: Cup): (Cup, Cup) { + let Cup(t) = c; + let half = t / 2; + let rem = if (t > 0) t - half else 0; + (cup::new(half), cup::new(rem)) + } + +} +``` + +And note that this `public use fun` does not create a circular dependency, as the `use fun` is not +present after the module is compiled--all methods are resolved statically. + +### Interactions with `use` Aliases + +A small detail to note is that method aliases respect normal `use` aliases. + +```move +module a::cup { + public struct Cup(T) has copy, drop, store; + + public fun cup_borrow(c: &Cup): &T { + &c.0 + } +} + +module b::other { + use a::cup::{Cup, cup_borrow as borrow}; + + fun example(c: &Cup) { + c.borrow(); // resolves to a::cup::cup_borrow + } +} +``` + +A helpful way to think about this is that `use` creates an implicit `use fun` alias for the function +whenever it can. In this case the `use a::cup::cup_borrow as borrow` creates an implicit +`use fun a::cup::cup_borrow as Cup.borrow` because it would be a valid `use fun` alias. Both views +are equivalent. This line of reasoning can inform how specific methods will resolve with shadowing. +See the cases in [Scoping](#scoping) for more details. + +### Scoping + +If not `public`, a `use fun` alias is local to its scope, much like a normal [`use`](uses.md). For +example + +```move +module a::m { + public struct X() has copy, drop, store; + public fun foo(_: &X) {} + public fun bar(_: &X) {} +} + +module b::other { + use a::m::X; + + use fun a::m::foo as X.f; + + fun example(x: &X) { + x.f(); // resolves to a::m::foo + { + use a::m::bar as f; + x.f(); // resolves to a::m::bar + }; + x.f(); // still resolves to a::m::foo + { + use fun a::m::bar as X.f; + x.f(); // resolves to a::m::bar + } + } +``` + +## Automatic Borrowing + +When resolving a method, the compiler will automatically borrow the receiver if the function expects +a reference. For example + +```move +module a::m { + public struct X() has copy, drop; + public fun by_val(_: X) {} + public fun by_ref(_: &X) {} + public fun by_mut(_: &mut X) {} + + fun example(mut x: X) { + x.by_ref(); // resolves to a::m::by_ref(&x) + x.by_mut(); // resolves to a::m::by_mut(&mut x) + } +} +``` + +In these examples, `x` was automatically borrowed to `&x` and `&mut x` respectively. This will also +work through field access + +```move +module a::m { + public struct X() has copy, drop; + public fun by_val(_: X) {} + public fun by_ref(_: &X) {} + public fun by_mut(_: &mut X) {} + + public struct Y has drop { x: X } + + fun example(mut y: Y) { + y.x.by_ref(); // resolves to a::m::by_ref(&y.x) + y.x.by_mut(); // resolves to a::m::by_mut(&mut y.x) + } +} +``` + +Note that in both examples, the local variable had to be labeled as [`mut`](./variables.md) to allow +for the `&mut` borrow. Without this, there would be an error saying that `x` (or `y` in the second +example) is not mutable. + +Keep in mind that without a reference, normal rules for variable and field access come into play. +Meaning a value might be moved or copied if it is not borrowed. + +```move +module a::m { + public struct X() has copy, drop; + public fun by_val(_: X) {} + public fun by_ref(_: &X) {} + public fun by_mut(_: &mut X) {} + + public struct Y has drop { x: X } + public fun drop_y(y: Y) { y } + + fun example(y: Y) { + y.x.by_val(); // copies `y.x` since `by_val` is by-value and `X` has `copy` + y.drop_y(); // moves `y` since `drop_y` is by-value and `Y` does _not_ have `copy` + } +} +``` + +## Chaining + +Method calls can be chained, because any expression can be the receiver of the method. + +```move +module a::shapes { + public struct Point has copy, drop, store { x: u64, y: u64 } + public struct Line has copy, drop, store { start: Point, end: Point } + + public fun x(p: &Point): u64 { p.x } + public fun y(p: &Point): u64 { p.y } + + public fun start(l: &Line): &Point { &l.start } + public fun end(l: &Line): &Point { &l.end } + +} + +module b::example { + use a::shapes::Line; + + public fun x_values(l: Line): (u64, u64) { + (l.start().x(), l.end().x()) + } + +} +``` + +In this example for `l.start().x()`, the compiler first resolves `l.start()` to +`a::shapes::start(&l)`. Then `.x()` is resolved to `a::shapes::x(a::shapes::start(&l))`. Similarly +for `l.end().x()`. Keep in mind, this feature is not "special"--the left-hand side of the `.` can be +any expression, and the compiler will resolve the method call as normal. We simply draw attention to +this sort of "chaining" because it is a common practice to increase readability. diff --git a/reference/src/modules.md b/reference/src/modules.md new file mode 100644 index 00000000..1e4a02a9 --- /dev/null +++ b/reference/src/modules.md @@ -0,0 +1,110 @@ +# Modules + +**Modules** are the core program unit that define types along with functions that operate on these +types. Struct types define the schema of Move's [storage](./abilities.md#key), and module functions +define the rules interacting with values of those types. While modules themselves are also stored in +storage, they are not accessible from within a Move program. In a blockchain environment, the +modules are stored on chain in a process typically referred to as "publishing". After being +published, [`entry`](./functions.md#entry-modifier) and [`public`](./functions.md#visibility) +functions can be invoked according to the rules of that particular Move instance. + +## Syntax + +A module has the following syntax: + +```text +module
:: { + ( | | | )* +} +``` + +where `
` is a valid [address](./primitive-types/address.md) specifying the module's +package. + +For example: + +```move +module 0x42::test { + public struct Example has copy, drop { i: u64 } + + use std::debug; + + const ONE: u64 = 1; + + public fun print(x: u64) { + let sum = x + ONE; + let example = Example { i: sum }; + debug::print(&sum) + } +} +``` + +## Names + +The `module test_addr::test` part specifies that the module `test` will be published under the +numerical [address](./primitive-types/address.md) value assigned for the name `test_addr` in the +[package settings](./packages.md). + +Modules should normally be declared using [named addresses](./primitive-types/address.md) (as +opposed to using the numerical value directly). For example: + +```move +module test_addr::test { + public struct Example has copy, drop { a: address } + + friend test_addr::another_test; + + public fun print() { + let example = Example { a: @test_addr }; + debug::print(&example) + } +} +``` + +These named addresses commonly match the name of the [package](./packages.md). + +Because named addresses only exist at the source language level and during compilation, named +addresses will be fully substituted for their value at the bytecode level. For example if we had the +following code: + +```move +fun example() { + my_addr::m::foo(@my_addr); +} +``` + +and we compiled it with `my_addr` set to `0xC0FFEE`, then it would be operationally equivalent to +the following: + +```move +fun example() { + 0xC0FFEE::m::foo(@0xC0FFEE); +} +``` + +While at the source level these two different accesses are equivalent, it is a best practice to +always use the named address and not the numerical value assigned to that address. + +Module names can start with a lowercase letter from `a` to `z` or an uppercase letter from `A` to +`Z`. After the first character, module names can contain underscores `_`, letters `a` to `z`, +letters `A` to `Z`, or digits `0` to `9`. + +```move +module a::my_module {} +module a::foo_bar_42 {} +``` + +Typically, module names start with a lowercase letter. A module named `my_module` should be stored +in a source file named `my_module.move`. + +## Members + +All members inside a `module` block can appear in any order. Fundamentally, a module is a collection +of [`types`](./structs.md) and [`functions`](./functions.md). The [`use`](./uses.md) keyword refers +to members from other modules. The [`const`](./constants.md) keyword defines constants that can be +used in the functions of a module. + +The [`friend`](./friends.md) syntax is a deprecated concept for specifying a list of trusted +modules. The concept has been superceded by [`public(package)`](./functions.md#visibility) + + diff --git a/reference/src/overview.md b/reference/src/overview.md new file mode 100644 index 00000000..6cffb03f --- /dev/null +++ b/reference/src/overview.md @@ -0,0 +1,163 @@ +--- +id: overview +title: Overview +sidebar_label: Move +--- + +Move is a next generation language for secure asset programming. Its primary use case is in +blockchain environments, where Move programs are used to construct state changes. Move allows +developers to write programs that flexibly manage and transfer assets, while providing the security +and protections against attacks on those assets. + +### Start Here + + + + + + +### Primitive Types + + + + + + + + + + +### Basic Concepts + + + + + + + + + + + + + + +### Reference + + + + diff --git a/reference/src/packages.md b/reference/src/packages.md new file mode 100644 index 00000000..1bd8e7ec --- /dev/null +++ b/reference/src/packages.md @@ -0,0 +1,437 @@ +# Packages + +Packages allow Move programmers to more easily re-use code and share it across projects. The Move +package system allows programmers to easily: + +- Define a package containing Move code; +- Parameterize a package by [named addresses](./primitive-types/address.md); +- Import and use packages in other Move code and instantiate named addresses; +- Build packages and generate associated compilation artifacts from packages; and +- Work with a common interface around compiled Move artifacts. + +## Package Layout and Manifest Syntax + +A Move package source directory contains a `Move.toml` package manifest file, a +generated `Move.lock` file, and a set of subdirectories: + +``` +a_move_package +├── Move.toml (required) +├── Move.lock (generated) +├── sources (required) +├── doc_templates (optional) +├── examples (optional, test & dev mode) +└── tests (optional, test mode) +``` + + The directories and files labeled "required" must be present for a directory to be + considered a Move package and built. Optional directories may be present, + and if so, they will be included in the compilation process depending on the + mode used to build the package. For instance, when built in "dev" or "test" + modes, the `tests` and `examples` directories will also be included. + + Going through each of these in turn: + +1. The `Move.toml` file is the package manifest and is required for a directory + to be considered a Move package. This file contains metadata about the + package, such as name, dependencies, and so on. +2. The `Move.lock` file is generated by the Move CLI and contains the fixed + build versions of the package and its dependencies. It is used to ensure + consistent versions are used across different builds and that changes in + dependencies are apparent as a change in this file. +3. The `sources` directory is required and contains the Move modules that make + up the package. Modules in this directory will always be included in the + compilation process. +4. The `doc_templates` directory can contain documentation templates that will + be used when generating documentation for the package. +5. The `examples` directory can hold additional code to be used only for + development and/or tutorials, this will not be included when compiled + outside of `test` or `dev` modes. +6. The `tests` directory can contain Move modules that are only included when + compiled in `test` mode or when [Move unit tests](./unit-testing.md) are + run. + +### Move.toml + +The Move package manifest is defined within the `Move.toml` file and has the following syntax. +Optional fields are marked with `*`, `+` denotes one or more elements: + +``` +[package] +name = +edition* = # e.g., "2024.alpha" to use the Move 2024 edition, + # currently in alpha. Will default to the latest stable edition if not specified. +license* = # e.g., "MIT", "GPL", "Apache 2.0" +authors* = [,+] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +# Additional fields may be added to this section by external tools. E.g., on Sui the following sections are added: +published-at* = "" # The address that the package is published at. Should be set after the first publication. + +[dependencies] # (Optional section) Paths to dependencies +# One or more lines declaring dependencies in the following format + +# ##### Local Dependencies ##### +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } +# To resolve a version conflict and force a specific version for dependency +# override you can use `override = true` +# Override = { local = "../conflicting/version", override = true } +# To instantiate address values in a dependency, use `addr_subst` + = { + local = , + override* = , + addr_subst* = { ( = ( | ""))+ } +} + +# ##### Git Dependencies ##### +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision must be supplied, it can be a branch, a tag, or a commit hash. +# If no `subdir` is specified, the root of the repository is used. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + = { + git = , + subdir=, + rev=, + override* = , + addr_subst* = { ( = ( | ""))+ } +} + +[addresses] # (Optional section) Declares named addresses in this package +# One or more lines declaring named addresses in the following format +# Addresses that match the name of the package must be set to `"0x0"` or they will be unable to be published. + = "_" | "" # e.g., std = "_" or my_addr = "0xC0FFEECAFE" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] # (Optional section) Same as [dependencies] section, but only included in "dev" and "test" modes +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can e.g., introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + = { + local = , + override* = , + addr_subst* = { ( = ( | ""))+ } +} + = { + git = , + subdir=, + rev=, + override* = , + addr_subst* = { ( = ( | ""))+ } +} + +[dev-addresses] # (Optional section) Same as [addresses] section, but only included in "dev" and "test" modes +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. + = "" # e.g., alice = "0xB0B" +``` + +An example of a minimal package manifest: + +``` +[package] +name = "AName" +``` + +An example of a more standard package manifest that also includes the Move standard library and +instantiates the named address `std` from the `LocalDep` package with the address value `0x1`: + +``` +[package] +name = "AName" +license = "Apache 2.0" + +[addresses] +address_to_be_filled_in = "_" +specified_address = "0xB0B" + +[dependencies] +# Local dependency +LocalDep = { local = "projects/move-awesomeness", addr_subst = { "std" = "0x1" } } +# Git dependency +MoveStdlib = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "framework/mainnet" } + +[dev-addresses] # For use when developing this module +address_to_be_filled_in = "0x101010101" +``` + +Most of the sections in the package manifest are self explanatory, but named +addresses can be a bit difficult to understand so we examine them in more +detail in [Named Addresses During +Compilation](#named-addresses-during-compilation), but before that we'll first +take a look at the `Move.lock` file and what it contains. + +## Move.lock + +The `Move.lock` file is generated at the root of the Move pacakge when the +package is built. The `Move.lock` file contains information about your package +and its build configuration, and acts as a communication layer between the Move +compiler and other tools, like chain-specific command line interfaces and +third-party package managers. + +Like the `Move.toml` file, the `Move.lock` file is a text-based TOML file. +Unlike the package manifest however, the `Move.lock` file is not intended for +you to edit directly. Processes on the toolchain, like the Move compiler, +access and edit the file to read and append relevant information to it. You +also must not move the file from the root, as it needs to be at the same level +as the `Move.toml` manifest in the package. + +If you are using source control for your package, it's recommended practice to +check in the `Move.lock` file that corresponds with your desired built or +published package. This ensures that every build of your package is an exact replica +of the original, and that changes to the build will be apparent as changes to +the `Move.lock` file. + +The `Move.lock` file is a TOML file that currently contains the following +fields. + +**Note**: other fields may be added to the lock file either in the future, or +by third-party package package managers as well. + +### The `[move]` Section + +This section contains the core information needed in the lockfile: +* The version of the lockfile (needed for backwards compatibility checking, and + versioning lockfile changes in the future). +* The hash of the `Move.toml` file that was used to generate this lock file. +* The hash of the `Move.lock` file of all dependencies. If no depencies are + present, this will be an empty string. +* The list of dependencies. + +``` +[move] +version = # Lock file version, used for backwards compatibility checking. +manifest_digest = # Sha3-256 hash of the Move.toml file that was used to generate this lock file. +deps_digest = # Sha3-256 hash of the Move.lock file of all dependencies. If no depencies are present, this will be an empty string. +dependencies = { (name = )* } # List of dependencies. Not present if there are no dependencies. +``` + +### The `[move.package]` Sections + +After the Move compiler resolves each of the dependencies for the package it +writes the location of the dependency to the `Move.lock` file. If a dependency +failed to resolve, the compiler will not write the `Move.lock` file and the +build fails. If all dependencies resolve, the `Move.lock` file contains the +locations (local and remote) of all of the package's transitive dependencies. +These will be stored in the `Move.lock` file in the following format: + +``` +... + +[[move.package]] +name = "A" +source = { git = "https://github.com/b/c.git", subdir = "e/f", rev = "a1b2c3" } + +[[move.package]] +name = "B" +source = { local = "../local-dep" } +``` + +### The `[move.toolchain-version]` Section + +As mentioned above, additional fields may be added to the lock file by external +tools. For example, the Sui package manager adds toolchain version information +to the lock file that can then be used for on-chain source verification: + +``` +... + +[move.toolchain-version] +compiler-version = # The version of the Move compiler used to build the package, e.g. "1.21.0" +edition = # The edition of the Move language used to build the package, e.g. "2024.alpha" +flavor = # The flavor of the Move compiler used to build the package, e.g. "sui" +``` + +With that, let's now turn to the compilation process and how named addresses are +resolved, and how to use them. + +## Named Addresses During Compilation + +Recall that Move has [named addresses](./primitive-types/address.md) and that named addresses +cannot be declared in Move. Instead they are declared at the package level: in +the manifest file (`Move.toml`) for a Move package you declare named addresses +in the package, instantiate other named addresses, and rename named addresses +from other packages within the Move package system. + +Let's go through each of these actions, and how they are performed in the +package's manifest one-by-one: + +### Declaring Named Addresses + +Let's say we have a Move module in `example_pkg/sources/A.move` as follows: + +```move +module named_addr::a { + public fun x(): address { @named_addr } +} +``` + +We could in `example_pkg/Move.toml` declare the named address `named_addr` in two different ways. +The first: + +``` +[package] +name = "example_pkg" +... +[addresses] +named_addr = "_" +``` + +Declares `named_addr` as a named address in the package `example_pkg` and that _this address can be +any valid address value_. In particular, an importing package can pick the value of the named address +`named_addr` to be any address it wishes. Intuitively you can think of this as parameterizing the +package `example_pkg` by the named address `named_addr`, and the package can then be instantiated +later on by an importing package. + +`named_addr` can also be declared as: + +``` +[package] +name = "example_pkg" +... +[addresses] +named_addr = "0xCAFE" +``` + +which states that the named address `named_addr` is exactly `0xCAFE` and cannot be changed. This is +useful so other importing packages can use this named address without needing to worry about the +exact value assigned to it. + +With these two different declaration methods, there are two ways that information about named +addresses can flow in the package graph: + +- The former ("unassigned named addresses") allows named address values to flow from the importation + site to the declaration site. +- The latter ("assigned named addresses") allows named address values to flow from the declaration + site upwards in the package graph to usage sites. + +With these two methods for flowing named address information throughout the package graph the rules +around scoping and renaming become important to understand. + +## Scope and Renaming of Named Addresses + +A named address `N` in a package `P` is in scope if: + +1. `P` declares a named address `N`; or +2. A package in one of `P`'s transitive dependencies declares the named address `N` and there is a + dependency path in the package graph between between `P` and the declaring package of `N` with no + renaming of `N`. + +Additionally, every named address in a package is exported. Because of this and the above scoping +rules each package can be viewed as coming with a set of named addresses that will be brought into +scope when the package is imported, e.g., if the `example_pkg` package was imported, that importation +would bring into scope the `named_addr` named address. Because of this, if `P` imports two packages +`P1` and `P2` both of which declare a named address `N` an issue arises in `P`: which "`N`" is meant +when `N` is referred to in `P`? The one from `P1` or `P2`? To prevent this ambiguity around which +package a named address is coming from, we enforce that the sets of scopes introduced by all +dependencies in a package are disjoint, and provide a way to _rename named addresses_ when the +package that brings them into scope is imported. + +Renaming a named address when importing can be done as follows in our `P`, `P1`, and `P2` example +above: + +``` +[package] +name = "P" +... +[dependencies] +P1 = { local = "some_path_to_P1", addr_subst = { "P1N" = "N" } } +P2 = { local = "some_path_to_P2" } +``` + +With this renaming `N` refers to the `N` from `P2` and `P1N` will refer to `N` coming from `P1`: + +```move +module N::A { + public fun x(): address { @P1N } +} +``` + +It is important to note that _renaming is not local_: once a named address `N` has been renamed to +`N2` in a package `P` all packages that import `P` will not see `N` but only `N2` unless `N` is +reintroduced from outside of `P`. This is why rule (2) in the scoping rules at the start of this +section specifies a "dependency path in the package graph between between `P` and the declaring +package of `N` with no renaming of `N`." + +### Instantiating Named Addresses + +Named addresses can be instantiated multiple times across the package graph as long as it is always +with the same value. It is an error if the same named address (regardless of renaming) is +instantiated with differing values across the package graph. + +A Move package can only be compiled if all named addresses resolve to a value. This presents issues +if the package wishes to expose an uninstantiated named address. This is what the `[dev-addresses]` +section solves in part. This section can set values for named addresses, but cannot introduce any named +addresses. Additionally, only the `[dev-addresses]` in the root package are included in `dev` mode. +For example a root package with the following manifest would not compile outside of `dev` mode since +`named_addr` would be uninstantiated: + +``` +[package] +name = "example_pkg" +... +[addresses] +named_addr = "_" + +[dev-addresses] +named_addr = "0xC0FFEE" +``` + +## Usage and Artifacts + +The Move package system comes with a command line option as part of the CLI: +`sui move `. Unless a particular path is provided, all +package commands will run in the current working directory. The full list of +commands and flags for the Move CLI can be found by running `sui move --help`. + +### Artifacts + +A package can be compiled using CLI commands. +This will create a `build` +directory containing build-related artifacts (such as bytecode, source maps, and +documentation). The general layout of the `build` directory is as follows: + +``` +a_move_package +├── BuildInfo.yaml +├── bytecode_modules +│   ├── dependencies +│   │   ├── +│   │   │   └── *.mv +│   │   ... +│   │   └── +│   │      └── *.mv +│   ... +│   └── *.mv +├── docs +│   ├── dependencies +│   │   ├── +│   │   │   └── *.md +│   │   ... +│   │   └── +│   │      └── *.md +│   ... +│   └── *.md +├── source_maps +│   ├── dependencies +│   │   ├── +│   │   │   └── *.mvsm +│   │   ... +│   │   └── +│   │      └── *.mvsm +│   ... +│   └── *.mvsm +└── sources +    ... +    └── *.move +    ├── dependencies +    │   ├── +    │   │   └── *.move +    │   ... +    │   └── +    │      └── *.move +    ... +    └── *.move +``` diff --git a/reference/src/primitive-types.md b/reference/src/primitive-types.md new file mode 100644 index 00000000..0540d98c --- /dev/null +++ b/reference/src/primitive-types.md @@ -0,0 +1,16 @@ +# Primitive Types + +The primitive types are the basic building blocks of the language. + +These primitive types can be used on their own or can be used be used to build more complex, +user-defined types, e.g. in a [`struct`](./structs.md). + +- [Integers](./primitive-types/integers.md) +- [Bool](./primitive-types/bool.md) +- [Address](./primitive-types/address.md) +- [Vector](./primitive-types/vector.md) + +These primitive types are used in conjunction with other types + +- [References](./primitive-types/references.md) +- [Tuples and Unit](./primitive-types/tuples.md) diff --git a/reference/src/primitive-types/address.md b/reference/src/primitive-types/address.md new file mode 100644 index 00000000..212dcd92 --- /dev/null +++ b/reference/src/primitive-types/address.md @@ -0,0 +1,82 @@ +# Address + +`address` is a built-in type in Move that is used to represent locations (sometimes called accounts) +in storage. An `address` value is a 256-bit (32 byte) identifier. Move uses addresses to +differentiate packages of [modules](../modules.md), where each package has its own address and +modules. Specific deployments of Move might also use the `address` value for +[storage](../abilities.md#key) operations. + +> For Sui, `address` is used to represent "accounts", and also objects via strong type wrappers +> (with `sui::object::UID` and `sui::object::ID`). + +Although an `address` is a 256 bit integer under the hood, Move addresses are intentionally +opaque---they cannot be created from integers, they do not support arithmetic operations, and they +cannot be modified. Specific deployments of Move might have `native` functions to enable some of +these operations (e.g., creating an `address` from bytes `vector`), but these are not part of +the Move language itself. + +While there are runtime address values (values of type `address`), they _cannot_ be used to access +modules at runtime. + +## Addresses and Their Syntax + +Addresses come in two flavors, named or numerical. The syntax for a named address follows the same +rules for any named identifier in Move. The syntax of a numerical address is not restricted to +hex-encoded values, and any valid [`u256` numerical value](./integers.md) can be used as an address +value, e.g., `42`, `0xCAFE`, and `10_000` are all valid numerical address literals. + +To distinguish when an address is being used in an expression context or not, the syntax when using +an address differs depending on the context where it's used: + +- When an address is used as an expression, the address must be prefixed by the `@` character, i.e., + [`@`](./integers.md) or `@`. +- Outside of expression contexts, the address may be written without the leading `@` character, + i.e., [``](./integers.md) or ``. + +In general, you can think of `@` as an operator that takes an address from being a namespace item to +being an expression item. + +## Named Addresses + +Named addresses are a feature that allow identifiers to be used in place of numerical values in any +spot where addresses are used, and not just at the value level. Named addresses are declared and +bound as top level elements (outside of modules and scripts) in Move packages, or passed as +arguments to the Move compiler. + +Named addresses only exist at the source language level and will be fully substituted for their +value at the bytecode level. Because of this, modules and module members should be accessed through +the module's named address and not through the numerical value assigned to the named address during +compilation. So while `use my_addr::foo` is equivalent to `use 0x2::foo` (if `my_addr` is assigned +`0x2`), it is a best practice to always use the `my_addr` name. + +### Examples + +```move +// shorthand for +// 0x0000000000000000000000000000000000000000000000000000000000000001 +let a1: address = @0x1; +// shorthand for +// 0x0000000000000000000000000000000000000000000000000000000000000042 +let a2: address = @0x42; +// shorthand for +// 0x00000000000000000000000000000000000000000000000000000000DEADBEEF +let a3: address = @0xDEADBEEF; +// shorthand for +// 0x000000000000000000000000000000000000000000000000000000000000000A +let a4: address = @0x0000000000000000000000000000000A; +// Assigns `a5` the value of the named address `std` +let a5: address = @std; +// Any valid numerical value can be used as an address +let a6: address = @66; +let a7: address = @42_000; + +module 66::some_module { // Not in expression context, so no @ needed + use 0x1::other_module; // Not in expression context so no @ needed + use std::vector; // Can use a named address as a namespace item + ... +} + +module std::other_module { // Can use a named address when declaring a module + ... +} +``` diff --git a/reference/src/primitive-types/bool.md b/reference/src/primitive-types/bool.md new file mode 100644 index 00000000..abbbbecc --- /dev/null +++ b/reference/src/primitive-types/bool.md @@ -0,0 +1,33 @@ +# Bool + +`bool` is Move's primitive type for boolean `true` and `false` values. + +## Literals + +Literals for `bool` are either `true` or `false`. + +## Operations + +### Logical + +`bool` supports three logical operations: + +| Syntax | Description | Equivalent Expression | +| ------------------------- | ---------------------------- | ------------------------------------------------------------------- | +| `&&` | short-circuiting logical and | `p && q` is equivalent to `if (p) q else false` | +| || | short-circuiting logical or | p || q is equivalent to `if (p) true else q` | +| `!` | logical negation | `!p` is equivalent to `if (p) false else true` | + +### Control Flow + +`bool` values are used in several of Move's control-flow constructs: + +- [`if (bool) { ... }`](../control-flow/conditionals.md) +- [`while (bool) { .. }`](../control-flow/loops.md) +- [`assert!(bool, u64)`](../abort-and-assert.md) + +## Ownership + +As with the other scalar values built-in to the language, boolean values are implicitly copyable, +meaning they can be copied without an explicit instruction such as +[`copy`](../variables.md#move-and-copy). diff --git a/reference/src/primitive-types/integers.md b/reference/src/primitive-types/integers.md new file mode 100644 index 00000000..cd254082 --- /dev/null +++ b/reference/src/primitive-types/integers.md @@ -0,0 +1,176 @@ +# Integers + +Move supports six unsigned integer types: `u8`, `u16`, `u32`, `u64`, `u128`, and `u256`. Values of +these types range from 0 to a maximum that depends on the size of the type. + +| Type | Value Range | +| -------------------------------- | ------------------------ | +| Unsigned 8-bit integer, `u8` | 0 to 28 - 1 | +| Unsigned 16-bit integer, `u16` | 0 to 216 - 1 | +| Unsigned 32-bit integer, `u32` | 0 to 232 - 1 | +| Unsigned 64-bit integer, `u64` | 0 to 264 - 1 | +| Unsigned 128-bit integer, `u128` | 0 to 2128 - 1 | +| Unsigned 256-bit integer, `u256` | 0 to 2256 - 1 | + +## Literals + +Literal values for these types are specified either as a sequence of digits (e.g.,`112`) or as hex +literals, e.g., `0xFF`. The type of the literal can optionally be added as a suffix, e.g., `112u8`. +If the type is not specified, the compiler will try to infer the type from the context where the +literal is used. If the type cannot be inferred, it is assumed to be `u64`. + +Number literals can be separated by underscores for grouping and readability. (e.g.,`1_234_5678`, +`1_000u128`, `0xAB_CD_12_35`). + +If a literal is too large for its specified (or inferred) size range, an error is reported. + +### Examples + +```move +// literals with explicit annotations; +let explicit_u8 = 1u8; +let explicit_u16 = 1u16; +let explicit_u32 = 1u32; +let explicit_u64 = 2u64; +let explicit_u128 = 3u128; +let explicit_u256 = 1u256; +let explicit_u64_underscored = 154_322_973u64; + +// literals with simple inference +let simple_u8: u8 = 1; +let simple_u16: u16 = 1; +let simple_u32: u32 = 1; +let simple_u64: u64 = 2; +let simple_u128: u128 = 3; +let simple_u256: u256 = 1; + +// literals with more complex inference +let complex_u8 = 1; // inferred: u8 +// right hand argument to shift must be u8 +let _unused = 10 << complex_u8; + +let x: u8 = 38; +let complex_u8 = 2; // inferred: u8 +// arguments to `+` must have the same type +let _unused = x + complex_u8; + +let complex_u128 = 133_876; // inferred: u128 +// inferred from function argument type +function_that_takes_u128(complex_u128); + +// literals can be written in hex +let hex_u8: u8 = 0x1; +let hex_u16: u16 = 0x1BAE; +let hex_u32: u32 = 0xDEAD80; +let hex_u64: u64 = 0xCAFE; +let hex_u128: u128 = 0xDEADBEEF; +let hex_u256: u256 = 0x1123_456A_BCDE_F; +``` + +## Operations + +### Arithmetic + +Each of these types supports the same set of checked arithmetic operations. For all of these +operations, both arguments (the left and right side operands) _must_ be of the same type. If you +need to operate over values of different types, you will need to first perform a [cast](#casting). +Similarly, if you expect the result of the operation to be too large for the integer type, perform a +[cast](#casting) to a larger size before performing the operation. + +All arithmetic operations abort instead of behaving in a way that mathematical integers would not +(e.g., overflow, underflow, divide-by-zero). + +| Syntax | Operation | Aborts If | +| ------ | ------------------- | ---------------------------------------- | +| `+` | addition | Result is too large for the integer type | +| `-` | subtraction | Result is less than zero | +| `*` | multiplication | Result is too large for the integer type | +| `%` | modular division | The divisor is `0` | +| `/` | truncating division | The divisor is `0` | + +### Bitwise + +The integer types support the following bitwise operations that treat each number as a series of +individual bits, either 0 or 1, instead of as numerical integer values. + +Bitwise operations do not abort. + +| Syntax | Operation | Description | +| ------------------- | ----------- | ----------------------------------------------------- | +| `&` | bitwise and | Performs a boolean and for each bit pairwise | +| | | bitwise or | Performs a boolean or for each bit pairwise | +| `^` | bitwise xor | Performs a boolean exclusive or for each bit pairwise | + +### Bit Shifts + +Similar to the bitwise operations, each integer type supports bit shifts. But unlike the other +operations, the righthand side operand (how many bits to shift by) must _always_ be a `u8` and need +not match the left side operand (the number you are shifting). + +Bit shifts can abort if the number of bits to shift by is greater than or equal to `8`, `16`, `32`, +`64`, `128` or `256` for `u8`, `u16`, `u32`, `u64`, `u128` and `u256` respectively. + +| Syntax | Operation | Aborts if | +| ------ | ----------- | ----------------------------------------------------------------------- | +| `<<` | shift left | Number of bits to shift by is greater than the size of the integer type | +| `>>` | shift right | Number of bits to shift by is greater than the size of the integer type | + +### Comparisons + +Integer types are the _only_ types in Move that can use the comparison operators. Both arguments +need to be of the same type. If you need to compare integers of different types, you must +[cast](#casting) one of them first. + +Comparison operations do not abort. + +| Syntax | Operation | +| ------ | ------------------------ | +| `<` | less than | +| `>` | greater than | +| `<=` | less than or equal to | +| `>=` | greater than or equal to | + +### Equality + +Like all types with [`drop`](../abilities.md), all integer types support the +["equal"](../equality.md) and ["not equal"](../equality.md) operations. Both arguments need to be of +the same type. If you need to compare integers of different types, you must [cast](#casting) one of +them first. + +Equality operations do not abort. + +| Syntax | Operation | +| ------ | --------- | +| `==` | equal | +| `!=` | not equal | + +For more details see the section on [equality](../equality.md) + +## Casting + +Integer types of one size can be cast to integer types of another size. Integers are the only types +in Move that support casting. + +Casts _do not_ truncate. Casting aborts if the result is too large for the specified type. + +| Syntax | Operation | Aborts if | +| ---------- | ---------------------------------------------------- | -------------------------------------- | +| `(e as T)` | Cast integer expression `e` into an integer type `T` | `e` is too large to represent as a `T` | + +Here, the type of `e` must be `8`, `16`, `32`, `64`, `128` or `256` and `T` must be `u8`, `u16`, +`u32`, `u64`, `u128`, or `u256`. + +For example: + +- `(x as u8)` +- `(y as u16)` +- `(873u16 as u32)` +- `(2u8 as u64)` +- `(1 + 3 as u128)` +- `(4/2 + 12345 as u256)` + +## Ownership + +As with the other scalar values built-in to the language, integer values are implicitly copyable, +meaning they can be copied without an explicit instruction such as +[`copy`](../variables.md#move-and-copy). diff --git a/reference/src/primitive-types/references.md b/reference/src/primitive-types/references.md new file mode 100644 index 00000000..c76cc3d4 --- /dev/null +++ b/reference/src/primitive-types/references.md @@ -0,0 +1,226 @@ +# References + +Move has two types of references: immutable `&` and mutable `&mut`. Immutable references are read +only, and cannot modify the underlying value (or any of its fields). Mutable references allow for +modifications via a write through that reference. Move's type system enforces an ownership +discipline that prevents reference errors. + +## Reference Operators + +Move provides operators for creating and extending references as well as converting a mutable +reference to an immutable one. Here and elsewhere, we use the notation `e: T` for "expression `e` +has type `T`". + +| Syntax | Type | Description | +| ----------- | ----------------------------------------------------- | -------------------------------------------------------------- | +| `&e` | `&T` where `e: T` and `T` is a non-reference type | Create an immutable reference to `e` | +| `&mut e` | `&mut T` where `e: T` and `T` is a non-reference type | Create a mutable reference to `e`. | +| `&e.f` | `&T` where `e.f: T` | Create an immutable reference to field `f` of struct `e`. | +| `&mut e.f` | `&mut T` where `e.f: T` | Create a mutable reference to field `f` of struct`e`. | +| `freeze(e)` | `&T` where `e: &mut T` | Convert the mutable reference `e` into an immutable reference. | + +The `&e.f` and `&mut e.f` operators can be used both to create a new reference into a struct or to +extend an existing reference: + +```move +let s = S { f: 10 }; +let f_ref1: &u64 = &s.f; // works +let s_ref: &S = &s; +let f_ref2: &u64 = &s_ref.f // also works +``` + +A reference expression with multiple fields works as long as both structs are in the same module: + +```move +public struct A { b: B } +public struct B { c : u64 } +fun f(a: &A): &u64 { + &a.b.c +} +``` + +Finally, note that references to references are not allowed: + +```move +let x = 7; +let y: &u64 = &x; +let z: &&u64 = &y; // ERROR! will not compile +``` + +## Reading and Writing Through References + +Both mutable and immutable references can be read to produce a copy of the referenced value. + +Only mutable references can be written. A write `*x = v` discards the value previously stored in `x` +and updates it with `v`. + +Both operations use the C-like `*` syntax. However, note that a read is an expression, whereas a +write is a mutation that must occur on the left hand side of an equals. + +| Syntax | Type | Description | +| ---------- | ----------------------------------- | ----------------------------------- | +| `*e` | `T` where `e` is `&T` or `&mut T` | Read the value pointed to by `e` | +| `*e1 = e2` | `()` where `e1: &mut T` and `e2: T` | Update the value in `e1` with `e2`. | + +In order for a reference to be read, the underlying type must have the +[`copy` ability](../abilities.md) as reading the reference creates a new copy of the value. This +rule prevents the copying of assets: + +```move= +fun copy_coin_via_ref_bad(c: Coin) { + let c_ref = &c; + let counterfeit: Coin = *c_ref; // not allowed! + pay(c); + pay(counterfeit); +} +``` + +Dually: in order for a reference to be written to, the underlying type must have the +[`drop` ability](../abilities.md) as writing to the reference will discard (or "drop") the old +value. This rule prevents the destruction of resource values: + +```move= +fun destroy_coin_via_ref_bad(mut ten_coins: Coin, c: Coin) { + let ref = &mut ten_coins; + *ref = c; // ERROR! not allowed--would destroy 10 coins! +} +``` + +## `freeze` inference + +A mutable reference can be used in a context where an immutable reference is expected: + +```move +let mut x = 7; +let y: &u64 = &mut x; +``` + +This works because the under the hood, the compiler inserts `freeze` instructions where they are +needed. Here are a few more examples of `freeze` inference in action: + +```move= +fun takes_immut_returns_immut(x: &u64): &u64 { x } + +// freeze inference on return value +fun takes_mut_returns_immut(x: &mut u64): &u64 { x } + +fun expression_examples() { + let mut x = 0; + let mut y = 0; + takes_immut_returns_immut(&x); // no inference + takes_immut_returns_immut(&mut x); // inferred freeze(&mut x) + takes_mut_returns_immut(&mut x); // no inference + + assert!(&x == &mut y, 42); // inferred freeze(&mut y) +} + +fun assignment_examples() { + let x = 0; + let y = 0; + let imm_ref: &u64 = &x; + + imm_ref = &x; // no inference + imm_ref = &mut y; // inferred freeze(&mut y) +} +``` + +### Subtyping + +With this `freeze` inference, the Move type checker can view `&mut T` as a subtype of `&T`. As shown +above, this means that anywhere for any expression where a `&T` value is used, a `&mut T` value can +also be used. This terminology is used in error messages to concisely indicate that a `&mut T` was +needed where a `&T` was supplied. For example + +```move= +module a::example { + fun read_and_assign(store: &mut u64, new_value: &u64) { + *store = *new_value + } + + fun subtype_examples() { + let mut x: &u64 = &0; + let mut y: &mut u64 = &mut 1; + + x = &mut 1; // valid + y = &2; // ERROR! invalid! + + read_and_assign(y, x); // valid + read_and_assign(x, y); // ERROR! invalid! + } +} +``` + +will yield the following error messages + +```text +error: + + ┌── example.move:11:9 ─── + │ + 12 │ y = &2; // invalid! + │ ^ Invalid assignment to local 'y' + · + 12 │ y = &2; // invalid! + │ -- The type: '&{integer}' + · + 9 │ let mut y: &mut u64 = &mut 1; + │ -------- Is not a subtype of: '&mut u64' + │ + +error: + + ┌── example.move:14:9 ─── + │ + 15 │ read_and_assign(x, y); // invalid! + │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call of 'a::example::read_and_assign'. Invalid argument for parameter 'store' + · + 8 │ let mut x: &u64 = &0; + │ ---- The type: '&u64' + · + 3 │ fun read_and_assign(store: &mut u64, new_value: &u64) { + │ -------- Is not a subtype of: '&mut u64' + │ +``` + +The only other types that currently have subtyping are [tuples](./tuples.md) + +## Ownership + +Both mutable and immutable references can always be copied and extended _even if there are existing +copies or extensions of the same reference_: + +```move +fun reference_copies(s: &mut S) { + let s_copy1 = s; // ok + let s_extension = &mut s.f; // also ok + let s_copy2 = s; // still ok + ... +} +``` + +This might be surprising for programmers familiar with Rust's ownership system, which would reject +the code above. Move's type system is more permissive in its treatment of +[copies](../variables.md#move-and-copy), but equally strict in ensuring unique ownership of mutable +references before writes. + +### References Cannot Be Stored + +References and tuples are the _only_ types that cannot be stored as a field value of structs, which +also means that they cannot exist in storage or [objects](../abilities/object.md). All references +created during program execution will be destroyed when a Move program terminates; they are entirely +ephemeral. This also applies to all types without the `store` ability: any value of a non-`store` +type must be destroyed before the program terminates. [ability](../abilities.md), but note that +references and tuples go a step further by never being allowed in structs in the first place. + +This is another difference between Move and Rust, which allows references to be stored inside of +structs. + +One could imagine a fancier, more expressive, type system that would allow references to be stored +in structs. We could allow references inside of structs that do not have the `store` +[ability](../abilities.md), but the core difficulty is that Move has a fairly complex system for +tracking static reference safety. This aspect of the type system would also have to be extended to +support storing references inside of structs. In short, Move's reference safety system would have to +expand to support stored references, and it is something we are keeping an eye on as the language +evolves. + + diff --git a/reference/src/primitive-types/tuples.md b/reference/src/primitive-types/tuples.md new file mode 100644 index 00000000..8941d214 --- /dev/null +++ b/reference/src/primitive-types/tuples.md @@ -0,0 +1,140 @@ +# Tuples and Unit + +Move does not fully support tuples as one might expect coming from another language with them as a +[first-class value](https://en.wikipedia.org/wiki/First-class_citizen). However, in order to support +multiple return values, Move has tuple-like expressions. These expressions do not result in a +concrete value at runtime (there are no tuples in the bytecode), and as a result they are very +limited: + +- They can only appear in expressions (usually in the return position for a function). +- They cannot be bound to local variables. +- They cannot be bound to local variables. +- They cannot be stored in structs. +- Tuple types cannot be used to instantiate generics. + +Similarly, [unit `()`](https://en.wikipedia.org/wiki/Unit_type) is a type created by the Move source +language in order to be expression based. The unit value `()` does not result in any runtime value. +We can consider unit`()` to be an empty tuple, and any restrictions that apply to tuples also apply +to unit. + +It might feel weird to have tuples in the language at all given these restrictions. But one of the +most common use cases for tuples in other languages is for functions to allow functions to return +multiple values. Some languages work around this by forcing the users to write structs that contain +the multiple return values. However in Move, you cannot put references inside of +[structs](../structs.md). This required Move to support multiple return values. These multiple +return values are all pushed on the stack at the bytecode level. At the source level, these multiple +return values are represented using tuples. + +## Literals + +Tuples are created by a comma separated list of expressions inside of parentheses. + +| Syntax | Type | Description | +| --------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------ | +| `()` | `(): ()` | Unit, the empty tuple, or the tuple of arity 0 | +| `(e1, ..., en)` | `(e1, ..., en): (T1, ..., Tn)` where `e_i: Ti` s.t. `0 < i <= n` and `n > 0` | A `n`-tuple, a tuple of arity `n`, a tuple with `n` elements | + +Note that `(e)` does not have type `(e): (t)`, in other words there is no tuple with one element. If +there is only a single element inside of the parentheses, the parentheses are only used for +disambiguation and do not carry any other special meaning. + +Sometimes, tuples with two elements are called "pairs" and tuples with three elements are called +"triples." + +### Examples + +```move +module 0x42::example { + // all 3 of these functions are equivalent + + // when no return type is provided, it is assumed to be `()` + fun returns_unit_1() { } + + // there is an implicit () value in empty expression blocks + fun returns_unit_2(): () { } + + // explicit version of `returns_unit_1` and `returns_unit_2` + fun returns_unit_3(): () { () } + + + fun returns_3_values(): (u64, bool, address) { + (0, false, @0x42) + } + fun returns_4_values(x: &u64): (&u64, u8, u128, vector) { + (x, 0, 1, b"foobar") + } +} +``` + +## Operations + +The only operation that can be done on tuples currently is destructuring. + +### Destructuring + +For tuples of any size, they can be destructured in either a `let` binding or in an assignment. + +For example: + +```move +module 0x42::example { + // all 3 of these functions are equivalent + fun returns_unit() {} + fun returns_2_values(): (bool, bool) { (true, false) } + fun returns_4_values(x: &u64): (&u64, u8, u128, vector) { (x, 0, 1, b"foobar") } + + fun examples(cond: bool) { + let () = (); + let (mut x, mut y): (u8, u64) = (0, 1); + let (mut a, mut b, mut c, mut d) = (@0x0, 0, false, b""); + + () = (); + (x, y) = if (cond) (1, 2) else (3, 4); + (a, b, c, d) = (@0x1, 1, true, b"1"); + } + + fun examples_with_function_calls() { + let () = returns_unit(); + let (mut x, mut y): (bool, bool) = returns_2_values(); + let (mut a, mut b, mut c, mut d) = returns_4_values(&0); + + () = returns_unit(); + (x, y) = returns_2_values(); + (a, b, c, d) = returns_4_values(&1); + } +} +``` + +For more details, see [Move Variables](../variables.md). + +## Subtyping + +Along with references, tuples are the only types that have +[subtyping](https://en.wikipedia.org/wiki/Subtyping) in Move. Tuples have subtyping only in the +sense that subtype with references (in a covariant way). + +For example: + +```move +let x: &u64 = &0; +let y: &mut u64 = &mut 1; + +// (&u64, &mut u64) is a subtype of (&u64, &u64) +// since &mut u64 is a subtype of &u64 +let (a, b): (&u64, &u64) = (x, y); + +// (&mut u64, &mut u64) is a subtype of (&u64, &u64) +// since &mut u64 is a subtype of &u64 +let (c, d): (&u64, &u64) = (y, y); + +// ERROR! (&u64, &mut u64) is NOT a subtype of (&mut u64, &mut u64) +// since &u64 is NOT a subtype of &mut u64 +let (e, f): (&mut u64, &mut u64) = (x, y); +``` + +## Ownership + +As mentioned above, tuple values don't really exist at runtime. And currently they cannot be stored +into local variables because of this (but it is likely that this feature will come at some point in +the future). As such, tuples can only be moved currently, as copying them would require putting them +into a local variable first. diff --git a/reference/src/primitive-types/vector.md b/reference/src/primitive-types/vector.md new file mode 100644 index 00000000..730a7e4b --- /dev/null +++ b/reference/src/primitive-types/vector.md @@ -0,0 +1,180 @@ +# Vector + +`vector` is the only primitive collection type provided by Move. A `vector` is a homogenous +collection of `T`'s that can grow or shrink by pushing/popping values off the "end". + +A `vector` can be instantiated with any type `T`. For example, `vector`, `vector
`, +`vector<0x42::my_module::MyData>`, and `vector>` are all valid vector types. + +## Literals + +### General `vector` Literals + +Vectors of any type can be created with `vector` literals. + +| Syntax | Type | Description | +| --------------------- | ----------------------------------------------------------------------------- | ------------------------------------------ | +| `vector[]` | `vector[]: vector` where `T` is any single, non-reference type | An empty vector | +| `vector[e1, ..., en]` | `vector[e1, ..., en]: vector` where `e_i: T` s.t. `0 < i <= n` and `n > 0` | A vector with `n` elements (of length `n`) | + +In these cases, the type of the `vector` is inferred, either from the element type or from the +vector's usage. If the type cannot be inferred, or simply for added clarity, the type can be +specified explicitly: + +```move +vector[]: vector +vector[e1, ..., en]: vector +``` + +#### Example Vector Literals + +```move +(vector[]: vector); +(vector[0u8, 1u8, 2u8]: vector); +(vector[]: vector); +(vector
[@0x42, @0x100]: vector
); +``` + +### `vector` literals + +A common use-case for vectors in Move is to represent "byte arrays", which are represented with +`vector`. These values are often used for cryptographic purposes, such as a public key or a hash +result. These values are so common that specific syntax is provided to make the values more +readable, as opposed to having to use `vector[]` where each individual `u8` value is specified in +numeric form. + +There are currently two supported types of `vector` literals, _byte strings_ and _hex strings_. + +#### Byte Strings + +Byte strings are quoted string literals prefixed by a `b`, e.g. `b"Hello!\n"`. + +These are ASCII encoded strings that allow for escape sequences. Currently, the supported escape +sequences are: + +| Escape Sequence | Description | +| --------------- | ---------------------------------------------- | +| `\n` | New line (or Line feed) | +| `\r` | Carriage return | +| `\t` | Tab | +| `\\` | Backslash | +| `\0` | Null | +| `\"` | Quote | +| `\xHH` | Hex escape, inserts the hex byte sequence `HH` | + +#### Hex Strings + +Hex strings are quoted string literals prefixed by a `x`, e.g. `x"48656C6C6F210A"`. + +Each byte pair, ranging from `00` to `FF`, is interpreted as hex encoded `u8` value. So each byte +pair corresponds to a single entry in the resulting `vector`. + +#### Example String Literals + +```move +fun byte_and_hex_strings() { + assert!(b"" == x"", 0); + assert!(b"Hello!\n" == x"48656C6C6F210A", 1); + assert!(b"\x48\x65\x6C\x6C\x6F\x21\x0A" == x"48656C6C6F210A", 2); + assert!( + b"\"Hello\tworld!\"\n \r \\Null=\0" == + x"2248656C6C6F09776F726C6421220A200D205C4E756C6C3D00", + 3 + ); +} +``` + +## Operations + +`vector` supports the following operations via the `std::vector` module in the Move standard +library: + +| Function | Description | Aborts? | +| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | +| `vector::empty(): vector` | Create an empty vector that can store values of type `T` | Never | +| `vector::singleton(t: T): vector` | Create a vector of size 1 containing `t` | Never | +| `vector::push_back(v: &mut vector, t: T)` | Add `t` to the end of `v` | Never | +| `vector::pop_back(v: &mut vector): T` | Remove and return the last element in `v` | If `v` is empty | +| `vector::borrow(v: &vector, i: u64): &T` | Return an immutable reference to the `T` at index `i` | If `i` is not in bounds | +| `vector::borrow_mut(v: &mut vector, i: u64): &mut T` | Return a mutable reference to the `T` at index `i` | If `i` is not in bounds | +| `vector::destroy_empty(v: vector)` | Delete `v` | If `v` is not empty | +| `vector::append(v1: &mut vector, v2: vector)` | Add the elements in `v2` to the end of `v1` | Never | +| `vector::contains(v: &vector, e: &T): bool` | Return true if `e` is in the vector `v`. Otherwise, returns false | Never | +| `vector::swap(v: &mut vector, i: u64, j: u64)` | Swaps the elements at the `i`th and `j`th indices in the vector `v` | If `i` or `j` is out of bounds | +| `vector::reverse(v: &mut vector)` | Reverses the order of the elements in the vector `v` in place | Never | +| `vector::index_of(v: &vector, e: &T): (bool, u64)` | Return `(true, i)` if `e` is in the vector `v` at index `i`. Otherwise, returns `(false, 0)` | Never | +| `vector::remove(v: &mut vector, i: u64): T` | Remove the `i`th element of the vector `v`, shifting all subsequent elements. This is O(n) and preserves ordering of elements in the vector | If `i` is out of bounds | +| `vector::swap_remove(v: &mut vector, i: u64): T` | Swap the `i`th element of the vector `v` with the last element and then pop the element, This is O(1), but does not preserve ordering of elements in the vector | If `i` is out of bounds | + + + +More operations may be added over time. + +## Example + +```move +use std::vector; + +let mut v = vector::empty(); +vector::push_back(&mut v, 5); +vector::push_back(&mut v, 6); + +assert!(*vector::borrow(&v, 0) == 5, 42); +assert!(*vector::borrow(&v, 1) == 6, 42); +assert!(vector::pop_back(&mut v) == 6, 42); +assert!(vector::pop_back(&mut v) == 5, 42); +``` + +## Destroying and copying `vector`s + +Some behaviors of `vector` depend on the abilities of the element type, `T`. For example, vectors +containing elements that do not have `drop` cannot be implicitly discarded like `v` in the example +above--they must be explicitly destroyed with `vector::destroy_empty`. + +Note that `vector::destroy_empty` will abort at runtime unless `vec` contains zero elements: + +```move +fun destroy_any_vector(vec: vector) { + vector::destroy_empty(vec) // deleting this line will cause a compiler error +} +``` + +But no error would occur for dropping a vector that contains elements with `drop`: + +```move +fun destroy_droppable_vector(vec: vector) { + // valid! + // nothing needs to be done explicitly to destroy the vector +} +``` + +Similarly, vectors cannot be copied unless the element type has `copy`. In other words, a +`vector` has `copy` if and only if `T` has `copy`. Note that it will be implicitly copied if +needed: + +```move +let x = vector[10]; +let y = x; // implicit copy +let z = x; +(y, z) +``` + +Keep in mind, copies of large vectors can be expensive. If this is a concern, annotating the +`intended` usage can prevent accidental copies. For example, + +```move +let x = vector[10]; +let y = move x; +let z = x; // ERROR! x has been moved +(y, z) +``` + +For more details see the sections on [type abilities](../abilities.md) and +[generics](../generics.md). + +## Ownership + +As mentioned [above](#destroying-and-copying-vectors), `vector` values can be copied only if the +elements can be copied. In that case, the copy can be done via a +[`copy`](../variables.md#move-and-copy) or a +[dereference `*`](./references.md#reading-and-writing-through-references). diff --git a/reference/src/structs.md b/reference/src/structs.md new file mode 100644 index 00000000..fc282341 --- /dev/null +++ b/reference/src/structs.md @@ -0,0 +1,479 @@ +# Structs and Resources + +A _struct_ is a user-defined data structure containing typed fields. Structs can store any +non-reference, non-tuple type, including other structs. + +Structs can be used to define all "asset" values or unrestricted values, where the operations +performed on those values can be controlled by the struct's [abilities](./abilities.md). By default, +structs are linear and ephemeral. By this we mean that they: cannot be copied, cannot be dropped, +and cannot be stored in storage. This means that all values have to have ownership transferred +(linear) and the values must be dealt with by the end of the program's execution (ephemeral). We can +relax this behavior by giving the struct [abilities](./abilities.md) which allow values to be copied +or dropped and also to be stored in storage or to define storage schemas. + +## Defining Structs + +Structs must be defined inside a module, and the struct's fields can either be named or positional: + +```move +module a::m { + public struct Foo { x: u64, y: bool } + public struct Bar {} + public struct Baz { foo: Foo, } + // ^ note: it is fine to have a trailing comma + + public struct PosFoo(u64, bool) + public struct PosBar() + public struct PosBaz(Foo) +} +``` + +Structs cannot be recursive, so the following definitions are invalid: + +```move +public struct Foo { x: Foo } +// ^ ERROR! recursive definition + +public struct A { b: B } +public struct B { a: A } +// ^ ERROR! recursive definition + +public struct D(D) +// ^ ERROR! recursive definition +``` + +### Visibility + +As you may have noticed, all structs are declared as `public`. This means that the type of the +struct can be referred to from any other module. However, the fields of the struct, and the ability +to create or destroy the struct, are still internal to the module that defines the struct. + +In the future, we plan on adding to declare structs as `public(package)` or as internal, much like +[functions](./functions.md#visibility). + +### Abilities + +As mentioned above: by default, a struct declaration is linear and ephemeral. So to allow the value +to be used in these ways (e.g., copied, dropped, stored in an [object](./abilities/object.md), or +used to define a storable [object](./abilities/object.md)), structs can be granted +[abilities](./abilities.md) by annotating them with `has `: + +```move +module a::m { + public struct Foo has copy, drop { x: u64, y: bool } +} +``` + +The ability declaration can occur either before or after the struct's fields. However, only one or +the other can be used, and not both. If declared after the struct's fields, the ability declaration +must be terminated with a semicolon: + +```move +module a::m { + public PreNamedAbilities has copy, drop { x: u64, y: bool } + public struct PostNamedAbilities { x: u64, y: bool } has copy, drop; + public struct PostNamedAbilitiesInvalid { x: u64, y: bool } has copy, drop + // ^ ERROR! missing semicolon + + public struct NamedInvalidAbilities has copy { x: u64, y: bool } has drop; + // ^ ERROR! duplicate ability declaration + + public PrePositionalAbilities has copy, drop (u64, bool) + public struct PostPositionalAbilities (u64, bool) has copy, drop; + public struct PostPositionalAbilitiesInvalid (u64, bool) has copy, drop + // ^ ERROR! missing semicolon + public struct InvalidAbilities has copy (u64, bool) has drop; + // ^ ERROR! duplicate ability declaration +} +``` + +For more details, see the section on +[annotating a struct's abilities](./abilities.md#annotating-structs). + +### Naming + +Structs must start with a capital letter `A` to `Z`. After the first letter, struct names can +contain underscores `_`, letters `a` to `z`, letters `A` to `Z`, or digits `0` to `9`. + +```move +public struct Foo {} +public struct BAR {} +public struct B_a_z_4_2 {} +public struct P_o_s_Foo() +``` + +This naming restriction of starting with `A` to `Z` is in place to give room for future language +features. It may or may not be removed later. + +## Using Structs + +### Creating Structs + +Values of a struct type can be created (or "packed") by indicating the struct name, followed by +value for each field. + +For a struct with named fields, the order of the fields does not matter, but the field name needs to +be provided. For a struct with positional fields, the order of the fields must match the order of +the fields in the struct definition, and it must be created using `()` instead of `{}` to enclose +the parameters. + +```move +module a::m { + public struct Foo has drop { x: u64, y: bool } + public struct Baz has drop { foo: Foo } + public struct Positional(u64, bool) has drop; + + fun example() { + let foo = Foo { x: 0, y: false }; + let baz = Baz { foo: foo }; + // Note: positional struct values are created using parentheses and + // based on position instead of name. + let pos = Positional(0, false); + let pos_invalid = Positional(false, 0); + // ^ ERROR! Fields are out of order and the types don't match. + } +} +``` + +For structs with named fields, you can use the following shorthand if you have a local variable with +the same name as the field: + +```move +let baz = Baz { foo: foo }; +// is equivalent to +let baz = Baz { foo }; +``` + +This is sometimes called "field name punning". + +### Destroying Structs via Pattern Matching + +Struct values can be destroyed by binding or assigning them in patterns using similar syntax to +constructing them. + +```move +module a::m { + public struct Foo { x: u64, y: bool } + public struct Bar(Foo) + public struct Baz {} + public struct Qux() + + fun example_destroy_foo() { + let foo = Foo { x: 3, y: false }; + let Foo { x, y: foo_y } = foo; + // ^ shorthand for `x: x` + + // two new bindings + // x: u64 = 3 + // foo_y: bool = false + } + + fun example_destroy_foo_wildcard() { + let foo = Foo { x: 3, y: false }; + let Foo { x, y: _ } = foo; + + // only one new binding since y was bound to a wildcard + // x: u64 = 3 + } + + fun example_destroy_foo_assignment() { + let x: u64; + let y: bool; + Foo { x, y } = Foo { x: 3, y: false }; + + // mutating existing variables x and y + // x = 3, y = false + } + + fun example_foo_ref() { + let foo = Foo { x: 3, y: false }; + let Foo { x, y } = &foo; + + // two new bindings + // x: &u64 + // y: &bool + } + + fun example_foo_ref_mut() { + let foo = Foo { x: 3, y: false }; + let Foo { x, y } = &mut foo; + + // two new bindings + // x: &mut u64 + // y: &mut bool + } + + fun example_destroy_bar() { + let bar = Bar(Foo { x: 3, y: false }); + let Bar(Foo { x, y }) = bar; + // ^ nested pattern + + // two new bindings + // x: u64 = 3 + // y: bool = false + } + + fun example_destroy_baz() { + let baz = Baz {}; + let Baz {} = baz; + } + + fun example_destroy_qux() { + let qux = Qux(); + let Qux() = qux; + } +} +``` + +### Accessing Struct Fields + +Fields of a struct can be accessed using the dot operator `.`. + +For structs with named fields, the fields can be accessed by their name: + +```move +public struct Foo { x: u64, y: bool } +let foo = Foo { x: 3, y: true }; +let x = foo.x; // x == 3 +let y = foo.y; // y == true +``` + +For positional structs, fields can be accessed by their position in the struct definition: + +```move +public struct PosFoo(u64, bool) +let pos_foo = PosFoo(3, true); +let x = pos_foo.0; // x == 3 +let y = pos_foo.1; // y == true +``` + +Accessing struct fields without borrowing or copying them is subject to the field's ability +constraints. For more details see the sections on +[borrowing structs and fields](#borrowing-structs-and-fields) and +[reading and writing fields](#reading-and-writing-fields) for more information. + +### Borrowing Structs and Fields + +The `&` and `&mut` operator can be used to create references to structs or fields. These examples +include some optional type annotations (e.g., `: &Foo`) to demonstrate the type of operations. + +```move +let foo = Foo { x: 3, y: true }; +let foo_ref: &Foo = &foo; +let y: bool = foo_ref.y; // reading a field via a reference to the struct +let x_ref: &u64 = &foo.x; // borrowing a field by extending a reference to the struct + +let x_ref_mut: &mut u64 = &mut foo.x; +*x_ref_mut = 42; // modifying a field via a mutable reference +``` + +It is possible to borrow inner fields of nested structs: + +```move +let foo = Foo { x: 3, y: true }; +let bar = Bar(foo); + +let x_ref = &bar.0.x; +``` + +You can also borrow a field via a reference to a struct: + +```move +let foo = Foo { x: 3, y: true }; +let foo_ref = &foo; +let x_ref = &foo_ref.x; +// this has the same effect as let x_ref = &foo.x +``` + +### Reading and Writing Fields + +If you need to read and copy a field's value, you can then dereference the borrowed field: + +```move +let foo = Foo { x: 3, y: true }; +let bar = Bar(copy foo); +let x: u64 = *&foo.x; +let y: bool = *&foo.y; +let foo2: Foo = *&bar.0; +``` + +More canonically, the dot operator can be used to read fields of a struct without any borrowing. As +is true with +[dereferencing](./primitive-types/references.md#reading-and-writing-through-references), the field +type must have the `copy` [ability](./abilities.md). + +```move +let foo = Foo { x: 3, y: true }; +let x = foo.x; // x == 3 +let y = foo.y; // y == true +``` + +Dot operators can be chained to access nested fields: + +```move +let bar = Bar(Foo { x: 3, y: true }); +let x = baz.0.x; // x = 3; +``` + +However, this is not permitted for fields that contain non-primitive types, such a vector or another +struct: + +```move +let foo = Foo { x: 3, y: true }; +let bar = Bar(foo); +let foo2: Foo = *&bar.0; +let foo3: Foo = bar.0; // error! must add an explicit copy with *& +``` + +We can mutably borrow a field to a struct to assign it a new value: + +```move +let mut foo = Foo { x: 3, y: true }; +*&mut foo.x = 42; // foo = Foo { x: 42, y: true } +*&mut foo.y = !foo.y; // foo = Foo { x: 42, y: false } +let mut bar = Bar(foo); // bar = Bar(Foo { x: 42, y: false }) +*&mut bar.0.x = 52; // bar = Bar(Foo { x: 52, y: false }) +*&mut bar.0 = Foo { x: 62, y: true }; // bar = Bar(Foo { x: 62, y: true }) +``` + +Similar to dereferencing, we can instead directly use the dot operator to modify a field. And in +both cases, the field type must have the `drop` [ability](./abilities.md). + +```move +let mut foo = Foo { x: 3, y: true }; +foo.x = 42; // foo = Foo { x: 42, y: true } +foo.y = !foo.y; // foo = Foo { x: 42, y: false } +let mut bar = Bar(foo); // bar = Bar(Foo { x: 42, y: false }) +bar.0.x = 52; // bar = Bar(Foo { x: 52, y: false }) +bar.0 = Foo { x: 62, y: true }; // bar = Bar(Foo { x: 62, y: true }) +``` + +The dot syntax for assignment also works via a reference to a struct: + +```move +let foo = Foo { x: 3, y: true }; +let foo_ref = &mut foo; +foo_ref.x = foo_ref.x + 1; +``` + +## Privileged Struct Operations + +Most struct operations on a struct type `T` can only be performed inside the module that declares +`T`: + +- Struct types can only be created ("packed"), destroyed ("unpacked") inside the module that defines + the struct. +- The fields of a struct are only accessible inside the module that defines the struct. + +Following these rules, if you want to modify your struct outside the module, you will need to +provide public APIs for them. The end of the chapter contains some examples of this. + +However as stated [in the visibility section above](#visibility), struct _types_ are always visible +to another module + +```move +module a::m { + public struct Foo has drop { x: u64 } + + public fun new_foo(): Foo { + Foo { x: 42 } + } +} + +module a::n { + use a::m::Foo; + + public struct Wrapper has drop { + foo: Foo + // ^ valid the type is public + + } + + fun f1(foo: Foo) { + let x = foo.x; + // ^ ERROR! cannot access fields of `Foo` outside of `a::m` + } + + fun f2() { + let foo_wrapper = Wrapper { foo: m::new_foo() }; + // ^ valid the function is public + } +} + +``` + +## Ownership + +As mentioned above in [Defining Structs](#defining-structs), structs are by default linear and +ephemeral. This means they cannot be copied or dropped. This property can be very useful when +modeling real world assets like money, as you do not want money to be duplicated or get lost in +circulation. + +```move +module a::m { + public struct Foo { x: u64 } + + public fun copying() { + let foo = Foo { x: 100 }; + let foo_copy = copy foo; // ERROR! 'copy'-ing requires the 'copy' ability + let foo_ref = &foo; + let another_copy = *foo_ref // ERROR! dereference requires the 'copy' ability + } + + public fun destroying_1() { + let foo = Foo { x: 100 }; + + // error! when the function returns, foo still contains a value. + // This destruction requires the 'drop' ability + } + + public fun destroying_2(f: &mut Foo) { + *f = Foo { x: 100 } // error! + // destroying the old value via a write requires the 'drop' ability + } +} +``` + +To fix the example `fun destroying_1`, you would need to manually "unpack" the value: + +```move +module a::m { + public struct Foo { x: u64 } + + public fun destroying_1_fixed() { + let foo = Foo { x: 100 }; + let Foo { x: _ } = foo; + } +} +``` + +Recall that you are only able to deconstruct a struct within the module in which it is defined. This +can be leveraged to enforce certain invariants in a system, for example, conservation of money. + +If on the other hand, your struct does not represent something valuable, you can add the abilities +`copy` and `drop` to get a struct value that might feel more familiar from other programming +languages: + +```move +module a::m { + public struct Foo has copy, drop { x: u64 } + + public fun run() { + let foo = Foo { x: 100 }; + let foo_copy = foo; + // ^ this code copies foo, + // whereas `let x = move foo` would move foo + + let x = foo.x; // x = 100 + let x_copy = foo_copy.x; // x = 100 + + // both foo and foo_copy are implicitly discarded when the function returns + } +} +``` + +## Storage + +Structs can be used to define storage schemas, but the details are different per deployment of Move. +See the documentation for the [`key` ability](./abilities.md#key) and +[Sui objects](./abilities/object.md) for more details. diff --git a/reference/src/unit-testing.md b/reference/src/unit-testing.md new file mode 100644 index 00000000..cc50e807 --- /dev/null +++ b/reference/src/unit-testing.md @@ -0,0 +1,450 @@ +# Unit Tests + +Unit testing for Move uses three annotations in the Move source language: + +- `#[test]` +- `#[test_only]`, and +- `#[expected_failure]`. + +They respectively mark a function as a test, mark a module or module member (`use`, function, or +struct) as code to be included for testing only, and mark that a test is expected to fail. These +annotations can be placed on a function with any visibility. Whenever a module or module member is +annotated as `#[test_only]` or `#[test]`, it will not be included in the compiled bytecode unless it +is compiled for testing. + +## Test Annotations + +The `#[test]` annotation can only be placed on a function with no parameters. +This annotation marks the function as a test to be run by the unit testing harness. + +```move +#[test] // OK +fun this_is_a_test() { ... } + +#[test] // Will fail to compile since the test takes an argument +fun this_is_not_correct(arg: u64) { ... } +``` + +A test can also be annotated as an `#[expected_failure]`. This annotation marks +that the test is expected to raise an error. There are a number of options that +can be used with the `#[expected_failure]` annotation to ensure only a failure +with the specified condition is marked as passing, these options are detailed +in [Expected Failures](#expected-failures). Only functions that have the +`#[test]` annotation can also be annotated as an #`[expected_failure]`. + +Some simple examples of using the `#[expected_failure]` annotation are shown below: + +```move +#[test] +#[expected_failure] +public fun this_test_will_abort_and_pass() { abort 1 } + +#[test] +#[expected_failure] +public fun test_will_error_and_pass() { 1/0; } + +#[test] // Will pass since test fails with the expected abort code constant. +#[expected_failure(abort_code = ENotFound)] // ENotFound is a constant defined in the module +public fun test_will_error_and_pass_abort_code() { abort ENotFound } + +#[test] // Will fail since test fails with a different error than expected. +#[expected_failure(abort_code = my_module::EnotFound)] +public fun test_will_error_and_fail() { 1/0; } + +#[test, expected_failure] // Can have multiple in one attribute. This test will pass. +public fun this_other_test_will_abort_and_pass() { abort 1 } +``` + +## Expected Failures + +There are a number of different ways that you can use the `#[expected_failure]` +annotation to specify different types of error conditions. These are: + +### 1. `#[expected_failure(abort_code = )]` + +This will pass if the test aborts with the specified constant value in the +module that defines the constant and fail otherwise. This is the recommended +way of testing for expected test failures. + +**NOTE**: You can reference constants outside of the current module or package +in `expected_failure` annotations. + +```move +module pkg_addr::other_module { + const ENotFound: u64 = 1; + fun will_abort() { + abort ENotFound + } +} + +module pkg_addr::my_module { + use pkg_addr::other_module; + const ENotFound: u64 = 1; + + #[test] + #[expected_failure(abort_code = ENotFound)] + fun test_will_abort_and_pass() { abort ENotFound } + + #[test] + #[expected_failure(abort_code = other_module::ENotFound)] + fun test_will_abort_and_pass() { other_module::will_abort() } + + // FAIL: Will not pass since we are expecting the constant from the wrong module. + #[test] + #[expected_failure(abort_code = ENotFound)] + fun test_will_abort_and_pass() { other_module::will_abort() } +} +``` + +### 2. `#[expected_failure(arithmetic_error, location = )]` + +This specifies that the test is expected to fail with an arithmetic error +(e.g., integer overflow, division by zero, etc) at the specified location. The +`` must be a valid path to a module location, e.g., `Self`, or +`my_package::my_module`. + +```move +module pkg_addr::other_module { + fun will_arith_error() { 1/0; } +} + +module pkg_addr::my_module { + use pkg_addr::other_module; + #[test] + #[expected_failure(arithmetic_error, location = Self)] + fun test_will_arith_error_and_pass1() { 1/0; } + + #[test] + #[expected_failure(arithmetic_error, location = pkg_addr::other_module)] + fun test_will_arith_error_and_pass2() { other_module::will_arith_error() } + + // FAIL: Will fail since the location we expect it the fail at is different from where the test actually failed. + #[test] + #[expected_failure(arithmetic_error, location = Self)] + fun test_will_arith_error_and_fail() { other_module::will_arith_error() } +} +``` + +### 3. `#[expected_failure(out_of_gas, location = )]` + +This specifies that the test is expected to fail with an out of gas error at +the specified location. The `` must be a valid path to a module +location, e.g., `Self`, or `my_package::my_module`. + +```move +module pkg_addr::other_module { + fun will_oog() { loop {} } +} + +module pkg_addr::my_module { + use pkg_addr::other_module; + #[test] + #[expected_failure(out_of_gas, location = Self)] + fun test_will_oog_and_pass1() { loop {} } + + #[test] + #[expected_failure(arithmetic_error, location = pkg_addr::other_module)] + fun test_will_oog_and_pass2() { other_module::will_oog() } + + // FAIL: Will fail since the location we expect it the fail at is different from where the test actually failed. + #[test] + #[expected_failure(out_of_gas, location = Self)] + fun test_will_oog_and_fail() { other_module::will_oog() } +} +``` + +### 4. `#[expected_failure(vector_error, minor_status = , location = )]` + +This specifies that the test is expected to fail with a vector error at the +specified location and with the given `minor_status` if provided. The +`` must be a valid path to a module location, e.g., `Self`, or +`my_package::my_module`. The `` is an optional parameter that +specifies the minor status of the vector error. If it is not specified, the +test will pass if the test fails with any minor status. If it is specified, the +test will only pass if the test fails with a vector error with the specified +minor status. + +```move +module pkg_addr::other_module { + fun vector_borrow_empty() { + vector::borrow(&vector::empty(), 1); + } +} + +module pkg_addr::my_module { + #[test] + #[expected_failure(vector_error, location = Self)] + fun vector_abort_same_module() { + vector::borrow(&vector::empty(), 1); + } + + #[test] + #[expected_failure(vector_error, location = pkg_addr::other_module)] + fun vector_abort_same_module() { + other_module::vector_borrow_empty(); + } + + // Can specify minor statues (i.e., vector-specific error codes) to expect. + #[test] + #[expected_failure(vector_error, minor_status = 1, location = Self)] + fun native_abort_good_right_code() { + vector::borrow(&vector::empty(), 1); + } + + // FAIL: correct error, but wrong location. + #[test] + #[expected_failure(vector_error, location = pkg_addr::other_module)] + fun vector_abort_same_module() { + other_module::vector_borrow_empty(); + } + + // FAIL: correct error and location but the minor status differs so this test will fail. + #[test] + #[expected_failure(vector_error, minor_status = 0, location = Self)] + fun vector_abort_wrong_minor_code() { + vector::borrow(&vector::empty(), 1); + } +} +``` + +### 5. `#[expected_failure]` + +This will pass if the test aborts with any error code. Because of this you +should be incredibly careful using this way of annotating expected tests +failures, and instead prefer one of the ways described above instead. Examples +of these types of annotations are: + +```move +#[test] +#[expected_failure] +fun test_will_abort_and_pass1() { abort 1 } + +#[test] +#[expected_failure] +fun test_will_arith_error_and_pass2() { 1/0; } +``` + + +## Test Only Annotations + +A module and any of its members can be declared as test only. If an item is +annotated as `#[test_only]` the item will only be included in the compiled Move +bytecode when compiled in test mode. Additionally, when compiled outside of +test mode, any non-test `use`s of a `#[test_only]` module will raise an error +during compilation. + +**NOTE**: functions that are annotated with `#[test_only]` will only be available +to be called from test code, but they themselves are not tests and will not be +run as tests by the unit testing framework. + +```move +#[test_only] // test only attributes can be attached to modules +module abc { ... } + +#[test_only] // test only attributes can be attached to constants +const Addr: address = @0x1; + +#[test_only] // .. to uses +use pkg_addr::some_other_module; + +#[test_only] // .. to structs +public struct SomeStruct { ... } + +#[test_only] // .. and functions. Can only be called from test code, but this is _not_ a test! +fun test_only_function(...) { ... } +``` + +## Running Unit Tests + +Unit tests for a Move package can be run with the [`sui move test` command](./packages.md). + +When running tests, every test will either `PASS`, `FAIL`, or `TIMEOUT`. If a test case fails, the +location of the failure along with the function name that caused the failure will be reported if +possible. You can see an example of this below. + +A test will be marked as timing out if it exceeds the maximum number of +instructions that can be executed for any single test. This bound can be +changed using the options below. Additionally, while the result of a test is +always deterministic, tests are run in parallel by default, so the ordering of +test results in a test run is non-deterministic unless running with only one +thread (see `OPTIONS` below on how to do this). + +There are also a number of options that can be passed to the unit testing binary to fine-tune +testing and to help debug failing tests. The available options, and a +description of what each one can do can be found by passing the help flag to +the `sui move test` command: + +``` +$ sui move test --help +``` + +## Example + +A simple module using some of the unit testing features is shown in the following example: + +First create an empty package and change directory into it: + +```bash +$ sui move new test_example; cd test_example +``` + +Next add the following module under the `sources` directory: + +```move +// filename: sources/my_module.move +module test_example::my_module { + + public struct Wrapper(u64) + + const ECoinIsZero: u64 = 0; + + public fun make_sure_non_zero_coin(coin: Wrapper): Wrapper { + assert!(coin.0 > 0, ECoinIsZero); + coin + } + + #[test] + fun make_sure_non_zero_coin_passes() { + let coin = Wrapper(1); + let Wrapper(_) = make_sure_non_zero_coin(coin); + } + + #[test] + // Or #[expected_failure] if we don't care about the abort code + #[expected_failure(abort_code = ECoinIsZero)] + fun make_sure_zero_coin_fails() { + let coin = Wrapper(0); + let Wrapper(_) = make_sure_non_zero_coin(coin); + } + + #[test_only] // test only helper function + fun make_coin_zero(coin: &mut Wrapper) { + coin.0 = 0; + } + + #[test] + #[expected_failure(abort_code = ECoinIsZero)] + fun make_sure_zero_coin_fails2() { + let mut coin = Wrapper(10); + coin.make_coin_zero(); + let Wrapper(_) = make_sure_non_zero_coin(coin); + } +} +``` + +### Running Tests + +You can then run these tests with the `move test` command: + +``` +$ sui move test +INCLUDING DEPENDENCY Sui +INCLUDING DEPENDENCY MoveStdlib +BUILDING test_example +Running Move unit tests +[ PASS ] 0x0::my_module::make_sure_non_zero_coin_passes +[ PASS ] 0x0::my_module::make_sure_zero_coin_fails +[ PASS ] 0x0::my_module::make_sure_zero_coin_fails2 +Test result: OK. Total tests: 3; passed: 3; failed: 0 +``` + +### Using Test Flags + +#### Passing specific tests to run + +You can run a specific test, or a set of tests with `sui move test `. This +will only run tests whose fully qualified name contains ``. For example if +we wanted to only run tests with `"non_zero"` in their name: + +``` +$ sui move test non_zero +INCLUDING DEPENDENCY Sui +INCLUDING DEPENDENCY MoveStdlib +BUILDING test_example +Running Move unit tests +[ PASS ] 0x0::my_module::make_sure_non_zero_coin_passes +Test result: OK. Total tests: 1; passed: 1; failed: 0 +``` + +#### `-i ` or `--gas_used ` + +This bounds the amount of gas that can be consumed for any one test to ``: + +``` +$ sui move test -i 0 +INCLUDING DEPENDENCY Sui +INCLUDING DEPENDENCY MoveStdlib +BUILDING test_example +Running Move unit tests +[ TIMEOUT ] 0x0::my_module::make_sure_non_zero_coin_passes +[ FAIL ] 0x0::my_module::make_sure_zero_coin_fails +[ FAIL ] 0x0::my_module::make_sure_zero_coin_fails2 + +Test failures: + +Failures in 0x0::my_module: + +┌── make_sure_non_zero_coin_passes ────── +│ Test timed out +└────────────────── + + +┌── make_sure_zero_coin_fails ────── +│ error[E11001]: test failure +│ ┌─ ./sources/my_module.move:22:27 +│ │ +│ 21 │ fun make_sure_zero_coin_fails() { +│ │ ------------------------- In this function in 0x0::my_module +│ 22 │ let coin = MyCoin(0); +│ │ ^ Test did not error as expected. Expected test to abort with code 0 +│ +│ +└────────────────── + + +┌── make_sure_zero_coin_fails2 ────── +│ error[E11001]: test failure +│ ┌─ ./sources/my_module.move:34:31 +│ │ +│ 33 │ fun make_sure_zero_coin_fails2() { +│ │ -------------------------- In this function in 0x0::my_module +│ 34 │ let mut coin = MyCoin(10); +│ │ ^^ Test did not error as expected. Expected test to abort with code 0 +│ +│ +└────────────────── + +Test result: FAILED. Total tests: 3; passed: 0; failed: 3 +``` + +#### `-s` or `--statistics` + +With these flags you can gather statistics about the tests run and report the +runtime and gas used for each test. You can additionally add `csv` (`sui move +test -s csv`) to get the gas usage in a csv output format. For example, if we +wanted to see the statistics for the tests in the example above: + +``` +$ sui move test -s +INCLUDING DEPENDENCY Sui +INCLUDING DEPENDENCY MoveStdlib +BUILDING test_example +Running Move unit tests +[ PASS ] 0x0::my_module::make_sure_non_zero_coin_passes +[ PASS ] 0x0::my_module::make_sure_zero_coin_fails +[ PASS ] 0x0::my_module::make_sure_zero_coin_fails2 + +Test Statistics: + +┌────────────────────────────────────────────────┬────────────┬───────────────────────────┐ +│ Test Name │ Time │ Gas Used │ +├────────────────────────────────────────────────┼────────────┼───────────────────────────┤ +│ 0x0::my_module::make_sure_non_zero_coin_passes │ 0.001 │ 1 │ +├────────────────────────────────────────────────┼────────────┼───────────────────────────┤ +│ 0x0::my_module::make_sure_zero_coin_fails │ 0.001 │ 1 │ +├────────────────────────────────────────────────┼────────────┼───────────────────────────┤ +│ 0x0::my_module::make_sure_zero_coin_fails2 │ 0.001 │ 1 │ +└────────────────────────────────────────────────┴────────────┴───────────────────────────┘ + +Test result: OK. Total tests: 3; passed: 3; failed: 0 +``` diff --git a/reference/src/uses.md b/reference/src/uses.md new file mode 100644 index 00000000..6497a503 --- /dev/null +++ b/reference/src/uses.md @@ -0,0 +1,361 @@ +# Uses and Aliases + +The `use` syntax can be used to create aliases to members in other modules. `use` can be used to +create aliases that last either for the entire module, or for a given expression block scope. + +## Syntax + +There are several different syntax cases for `use`. Starting with the most simple, we have the +following for creating aliases to other modules + +```move +use
::; +use
:: as ; +``` + +For example + +```move +use std::vector; +use std::option as o; +``` + +`use std::vector;` introduces an alias `vector` for `std::vector`. This means that anywhere you +would want to use the module name `std::vector` (assuming this `use` is in scope), you could use +`vector` instead. `use std::vector;` is equivalent to `use std::vector as vector;` + +Similarly `use std::option as o;` would let you use `o` instead of `std::option` + +```move +use std::vector; +use std::option as o; + +fun new_vec(): vector> { + let mut v = vector[]; + vector::push_back(&mut v, o::some(0)); + vector::push_back(&mut v, o::none()); + v +} +``` + +If you want to import a specific module member (such as a function or struct). You can use the +following syntax. + +```move +use
::::; +use
:::: as ; +``` + +For example + +```move +use std::vector::push_back; +use std::option::some as s; +``` + +This would let you use the function `std::vector::push_back` without full qualification. Similarly +for `std::option::some` with `s`. Instead you could use `push_back` and `s` respectively. Again, +`use std::vector::push_back;` is equivalent to `use std::vector::push_back as push_back;` + +```move +use std::vector::push_back; +use std::option::some as s; + +fun new_vec(): vector> { + let mut v = vector[]; + vector::push_back(&mut v, s(0)); + vector::push_back(&mut v, std::option::none()); + v +} +``` + +### Multiple Aliases + +If you want to add aliases for multiple module members at once, you can do so with the following +syntax + +```move +use
::::{, as ... }; +``` + +For example + +```move +use std::vector::push_back; +use std::option::{some as s, none as n}; + +fun new_vec(): vector> { + let mut v = vector[]; + push_back(&mut v, s(0)); + push_back(&mut v, n()); + v +} +``` + +### Self aliases + +If you need to add an alias to the Module itself in addition to module members, you can do that in a +single `use` using `Self`. `Self` is a member of sorts that refers to the module. + +```move +use std::option::{Self, some, none}; +``` + +For clarity, all of the following are equivalent: + +```move +use std::option; +use std::option as option; +use std::option::Self; +use std::option::Self as option; +use std::option::{Self}; +use std::option::{Self as option}; +``` + +### Multiple Aliases for the Same Definition + +If needed, you can have as many aliases for any item as you like + +```move +use std::vector::push_back; +use std::option::{Option, some, none}; + +fun new_vec(): vector> { + let mut v = vector[]; + push_back(&mut v, some(0)); + push_back(&mut v, none()); + v +} +``` + +### Nested imports + +In Move, you can also import multiple names with the same `use` declaration. This brings all +provided names into scope: + +```move +use std::{ + vector::{Self as vec, push_back}, + string::{String, Self as str} +}; + +fun example(s: &mut String) { + let mut v = vec::empty(); + push_back(&mut v, 0); + push_back(&mut v, 10); + str::append_utf8(s, v); +} +``` + +## Inside a `module` + +Inside of a `module` all `use` declarations are usable regardless of the order of declaration. + +```move +module a::example { + use std::vector; + + fun new_vec(): vector> { + let mut v = vector[]; + vector::push_back(&mut v, 0); + vector::push_back(&mut v, 10); + v + } + + use std::option::{Option, some, none}; +} +``` + +The aliases declared by `use` in the module usable within that module. + +Additionally, the aliases introduced cannot conflict with other module members. See +[Uniqueness](#uniqueness) for more details + +## Inside an expression + +You can add `use` declarations to the beginning of any expression block + +```move +module a::example { + fun new_vec(): vector> { + use std::vector::push_back; + use std::option::{Option, some, none}; + + let mut v = vector[]; + push_back(&mut v, some(0)); + push_back(&mut v, none()); + v + } +} +``` + +As with `let`, the aliases introduced by `use` in an expression block are removed at the end of that +block. + +```move +module a::example { + fun new_vec(): vector> { + let result = { + use std::vector::push_back; + use std::option::{Option, some, none}; + + let mut v = vector[]; + push_back(&mut v, some(0)); + push_back(&mut v, none()); + v + }; + result + } +} +``` + +Attempting to use the alias after the block ends will result in an error + +```move + fun new_vec(): vector> { + let mut result = { + use std::vector::push_back; + use std::option::{Option, some, none}; + + let mut v = vector[]; + push_back(&mut v, some(0)); + v + }; + push_back(&mut result, std::option::none()); + // ^^^^^^ ERROR! unbound function 'push_back' + result + } +``` + +Any `use` must be the first item in the block. If the `use` comes after any expression or `let`, it +will result in a parsing error + +```move +{ + let mut v = vector[]; + use std::vector; // ERROR! +} +``` + +This allows you to shorten your import blocks in many situations. Note that these imports, as the +previous ones, are all subject to the naming and uniqueness rules described in the following +sections. + +## Naming rules + +Aliases must follow the same rules as other module members. This means that aliases to structs (and +constants) must start with `A` to `Z` + +```move +module a::data { + public struct S {} + const FLAG: bool = false; + public fun foo() {} +} +module a::example { + use a::data::{ + S as s, // ERROR! + FLAG as fLAG, // ERROR! + foo as FOO, // valid + foo as bar, // valid + }; +} +``` + +## Uniqueness + +Inside a given scope, all aliases introduced by `use` declarations must be unique. + +For a module, this means aliases introduced by `use` cannot overlap + +```move +module a::example { + use std::option::{none as foo, some as foo}; // ERROR! + // ^^^ duplicate 'foo' + + use std::option::none as bar; + + use std::option::some as bar; // ERROR! + // ^^^ duplicate 'bar' + +} +``` + +And, they cannot overlap with any of the module's other members + +```move +module a::data { + public struct S {} +} +module example { + use a::data::S; + + public struct S { value: u64 } // ERROR! + // ^ conflicts with alias 'S' above +} +} +``` + +Inside of an expression block, they cannot overlap with each other, but they can +[shadow](#shadowing) other aliases or names from an outer scope + +## Shadowing + +`use` aliases inside of an expression block can shadow names (module members or aliases) from the +outer scope. As with shadowing of locals, the shadowing ends at the end of the expression block; + +```move +module a::example { + + public struct WrappedVector { vec: vector } + + public fun empty(): WrappedVector { + WrappedVector { vec: std::vector::empty() } + } + + public fun push_back(v: &mut WrappedVector, value: u64) { + std::vector::push_back(&mut v.vec, value); + } + + fun example1(): WrappedVector { + use std::vector::push_back; + // 'push_back' now refers to std::vector::push_back + let mut vec = vector[]; + push_back(&mut vec, 0); + push_back(&mut vec, 1); + push_back(&mut vec, 10); + WrappedVector { vec } + } + + fun example2(): WrappedVector { + let vec = { + use std::vector::push_back; + // 'push_back' now refers to std::vector::push_back + + let mut v = vector[]; + push_back(&mut v, 0); + push_back(&mut v, 1); + v + }; + // 'push_back' now refers to Self::push_back + let mut res = WrappedVector { vec }; + push_back(&mut res, 10); + res + } +} +``` + +## Unused Use or Alias + +An unused `use` will result in a warning + +```move +module a::example { + use std::option::{some, none}; // Warning! + // ^^^^ unused alias 'none' + + public fun example(): std::option::Option { + some(0) + } +} +``` diff --git a/reference/src/variables.md b/reference/src/variables.md new file mode 100644 index 00000000..04ad0b44 --- /dev/null +++ b/reference/src/variables.md @@ -0,0 +1,796 @@ +# Local Variables and Scope + +Local variables in Move are lexically (statically) scoped. New variables are introduced with the +keyword `let`, which will shadow any previous local with the same name. Locals marked as `mut` are +mutable and can be updated both directly and via a mutable reference. + +## Declaring Local Variables + +### `let` bindings + +Move programs use `let` to bind variable names to values: + +```move +let x = 1; +let y = x + x: +``` + +`let` can also be used without binding a value to the local. + +```move +let x; +``` + +The local can then be assigned a value later. + +```move +let x; +if (cond) { + x = 1 +} else { + x = 0 +} +``` + +This can be very helpful when trying to extract a value from a loop when a default value cannot be +provided. + +```move +let x; +let cond = true; +let i = 0; +loop { + let (res, cond) = foo(i); + if (!cond) { + x = res; + break; + }; + i = i + 1; +} +``` + +To modify a local variable _after_ it is assigned, or to borrow it mutably `&mut`, it must be +declared as `mut`. + +```move +let mut x = 0; +if (cond) x = x + 1; +foo(&mut x); +``` + +For more details see the section on [assignments](#assignments) below. + +### Variables must be assigned before use + +Move's type system prevents a local variable from being used before it has been assigned. + +```move +let x; +x + x // ERROR! x is used before being assigned +``` + +```move +let x; +if (cond) x = 0; +x + x // ERROR! x does not have a value in all cases +``` + +```move +let x; +while (cond) x = 0; +x + x // ERROR! x does not have a value in all cases +``` + +### Valid variable names + +Variable names can contain underscores `_`, letters `a` to `z`, letters `A` to `Z`, and digits `0` +to `9`. Variable names must start with either an underscore `_` or a letter `a` through `z`. They +_cannot_ start with uppercase letters. + +```move +// all valid +let x = e; +let _x = e; +let _A = e; +let x0 = e; +let xA = e; +let foobar_123 = e; + +// all invalid +let X = e; // ERROR! +let Foo = e; // ERROR! +``` + +### Type annotations + +The type of a local variable can almost always be inferred by Move's type system. However, Move +allows explicit type annotations that can be useful for readability, clarity, or debuggability. The +syntax for adding a type annotation is: + +```move +let x: T = e; // "Variable x of type T is initialized to expression e" +``` + +Some examples of explicit type annotations: + +```move +module 0x42::example { + + public struct S { f: u64, g: u64 } + + fun annotated() { + let u: u8 = 0; + let b: vector = b"hello"; + let a: address = @0x0; + let (x, y): (&u64, &mut u64) = (&0, &mut 1); + let S { f, g: f2 }: S = S { f: 0, g: 1 }; + } +} +``` + +Note that the type annotations must always be to the right of the pattern: + +```move +// ERROR! should be let (x, y): (&u64, &mut u64) = ... +let (x: &u64, y: &mut u64) = (&0, &mut 1); +``` + +### When annotations are necessary + +In some cases, a local type annotation is required if the type system cannot infer the type. This +commonly occurs when the type argument for a generic type cannot be inferred. For example: + +```move +let _v1 = vector[]; // ERROR! +// ^^^^^^^^ Could not infer this type. Try adding an annotation +let v2: vector = vector[]; // no error +``` + +In a rarer case, the type system might not be able to infer a type for divergent code (where all the +following code is unreachable). Both [`return`](./functions.md#return-expression) and +[`abort`](./abort-and-assert.md) are expressions and can have any type. A +[`loop`](./control-flow/loops.md) has type `()` if it has a `break` (or `T` if has a `break e` where +`e: T`), but if there is no break out of the `loop`, it could have any type. If these types cannot +be inferred, a type annotation is required. For example, this code: + +```move +let a: u8 = return (); +let b: bool = abort 0; +let c: signer = loop (); + +let x = return (); // ERROR! +// ^ Could not infer this type. Try adding an annotation +let y = abort 0; // ERROR! +// ^ Could not infer this type. Try adding an annotation +let z = loop (); // ERROR! +// ^ Could not infer this type. Try adding an annotation +``` + +Adding type annotations to this code will expose other errors about dead code or unused local +variables, but the example is still helpful for understanding this problem. + +### Multiple declarations with tuples + +`let` can introduce more than one local at a time using tuples. The locals declared inside the +parenthesis are initialized to the corresponding values from the tuple. + +```move +let () = (); +let (x0, x1) = (0, 1); +let (y0, y1, y2) = (0, 1, 2); +let (z0, z1, z2, z3) = (0, 1, 2, 3); +``` + +The type of the expression must match the arity of the tuple pattern exactly. + +```move +let (x, y) = (0, 1, 2); // ERROR! +let (x, y, z, q) = (0, 1, 2); // ERROR! +``` + +You cannot declare more than one local with the same name in a single `let`. + +```move +let (x, x) = 0; // ERROR! +``` + +The mutability of the local variables declared can be mixed. + +```move +let (mut x, y) = (0, 1); +x = 1; +``` + +### Multiple declarations with structs + +`let` can also introduce more than one local at a time when destructuring (or matching against) a +struct. In this form, the `let` creates a set of local variables that are initialized to the values +of the fields from a struct. The syntax looks like this: + +```move +public struct T { f1: u64, f2: u64 } +``` + +```move +let T { f1: local1, f2: local2 } = T { f1: 1, f2: 2 }; +// local1: u64 +// local2: u64 +``` + +Similarly for positional structs + +```move +public struct P(u64, u64) +``` + +and + +```move +let P(local1, local2) = T { f1: 1, f2: 2 }; +// local1: u64 +// local2: u64 +``` + +Here is a more complicated example: + +```move +module 0x42::example { + public struct X(u64) + public struct Y { x1: X, x2: X } + + fun new_x(): X { + X(1) + } + + fun example() { + let Y { x1: X(f), x2 } = Y { x1: new_x(), x2: new_x() }; + assert!(f + x2.f == 2, 42); + + let Y { x1: X(f1), x2: X(f2) } = Y { x1: new_x(), x2: new_x() }; + assert!(f1 + f2 == 2, 42); + } +} +``` + +Fields of structs can serve double duty, identifying the field to bind _and_ the name of the +variable. This is sometimes referred to as punning. + +```move +let Y { x1, x2 } = e; +``` + +is equivalent to: + +```move +let Y { x1: x1, x2: x2 } = e; +``` + +As shown with tuples, you cannot declare more than one local with the same name in a single `let`. + +```move +let Y { x1: x, x2: x } = e; // ERROR! +``` + +And as with tuples, the mutability of the local variables declared can be mixed. + +```move +let Y { x1: mut x1, x2 } = e; +``` + +Furthermore, the mutability of annotation can be applied to the punned fields. Giving the equivalent +example + +```move +let Y { mut x1, x2 } = e; +``` + +### Destructuring against references + +In the examples above for structs, the bound value in the let was moved, destroying the struct value +and binding its fields. + +```move +public struct T { f1: u64, f2: u64 } +``` + +```move +let T { f1: local1, f2: local2 } = T { f1: 1, f2: 2 }; +// local1: u64 +// local2: u64 +``` + +In this scenario the struct value `T { f1: 1, f2: 2 }` no longer exists after the `let`. + +If you wish instead to not move and destroy the struct value, you can borrow each of its fields. For +example: + +```move +let t = T { f1: 1, f2: 2 }; +let T { f1: local1, f2: local2 } = &t; +// local1: &u64 +// local2: &u64 +``` + +And similarly with mutable references: + +```move +let mut t = T { f1: 1, f2: 2 }; +let T { f1: local1, f2: local2 } = &mut t; +// local1: &mut u64 +// local2: &mut u64 +``` + +This behavior can also work with nested structs. + +```move +module 0x42::example { + public struct X(u64) + public struct Y { x1: X, x2: X } + + fun new_x(): X { + X(1) + } + + fun example() { + let mut y = Y { x1: new_x(), x2: new_x() }; + + let Y { x1: X(f), x2 } = &y; + assert!(*f + x2.f == 2, 42); + + let Y { x1: X(f1), x2: X(f2) } = &mut y; + *f1 = *f1 + 1; + *f2 = *f2 + 1; + assert!(*f1 + *f2 == 4, 42); + } +} +``` + +### Ignoring Values + +In `let` bindings, it is often helpful to ignore some values. Local variables that start with `_` +will be ignored and not introduce a new variable + +```move +fun three(): (u64, u64, u64) { + (0, 1, 2) +} +``` + +```move +let (x1, _, z1) = three(); +let (x2, _y, z2) = three(); +assert!(x1 + z1 == x2 + z2, 42); +``` + +This can be necessary at times as the compiler will warn on unused local variables + +```move +let (x1, y, z1) = three(); // WARNING! +// ^ unused local 'y' +``` + +### General `let` grammar + +All of the different structures in `let` can be combined! With that we arrive at this general +grammar for `let` statements: + +> _let-binding_ → **let** _pattern-or-list_ _type-annotation__opt_ > +> _initializer__opt_ > _pattern-or-list_ → _pattern_ | **(** _pattern-list_ **)** > +> _pattern-list_ → _pattern_ **,**_opt_ | _pattern_ **,** _pattern-list_ > +> _type-annotation_ → **:** _type_ _initializer_ → **=** _expression_ + +The general term for the item that introduces the bindings is a _pattern_. The pattern serves to +both destructure data (possibly recursively) and introduce the bindings. The pattern grammar is as +follows: + +> _pattern_ → _local-variable_ | _struct-type_ **{** _field-binding-list_ **}** > +> _field-binding-list_ → _field-binding_ **,**_opt_ | _field-binding_ **,** > +> _field-binding-list_ > _field-binding_ → _field_ | _field_ **:** _pattern_ + +A few concrete examples with this grammar applied: + +```move + let (x, y): (u64, u64) = (0, 1); +// ^ local-variable +// ^ pattern +// ^ local-variable +// ^ pattern +// ^ pattern-list +// ^^^^ pattern-list +// ^^^^^^ pattern-or-list +// ^^^^^^^^^^^^ type-annotation +// ^^^^^^^^ initializer +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ let-binding + + let Foo { f, g: x } = Foo { f: 0, g: 1 }; +// ^^^ struct-type +// ^ field +// ^ field-binding +// ^ field +// ^ local-variable +// ^ pattern +// ^^^^ field-binding +// ^^^^^^^ field-binding-list +// ^^^^^^^^^^^^^^^ pattern +// ^^^^^^^^^^^^^^^ pattern-or-list +// ^^^^^^^^^^^^^^^^^^^^ initializer +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ let-binding +``` + +## Mutations + +### Assignments + +After the local is introduced (either by `let` or as a function parameter), a `mut` local can be +modified via an assignment: + +```move +x = e +``` + +Unlike `let` bindings, assignments are expressions. In some languages, assignments return the value +that was assigned, but in Move, the type of any assignment is always `()`. + +```move +(x = e: ()) +``` + +Practically, assignments being expressions means that they can be used without adding a new +expression block with braces (`{`...`}`). + +```move +let x; +if (cond) x = 1 else x = 2; +``` + +The assignment uses the similar pattern syntax scheme as `let` bindings, but with absence of `mut`: + +```move +module 0x42::example { + public struct X { f: u64 } + + fun new_x(): X { + X { f: 1 } + } + + // Note: this example will complain about unused variables and assignments. + fun example() { + let (mut x, mut y, mut f, mut g) = (0, 0, 0, 0); + + (X { f }, X { f: x }) = (new_x(), new_x()); + assert!(f + x == 2, 42); + + (x, y, f, _, g) = (0, 0, 0, 0, 0); + } +} +``` + +Note that a local variable can only have one type, so the type of the local cannot change between +assignments. + +```move +let mut x; +x = 0; +x = false; // ERROR! +``` + +### Mutating through a reference + +In addition to directly modifying a local with assignment, a `mut` local can be modified via a +mutable reference `&mut`. + +```move +let mut x = 0; +let r = &mut x; +*r = 1; +assert!(x == 1, 42); +``` + +This is particularly useful if either: + +(1) You want to modify different variables depending on some condition. + +```move +let mut x = 0; +let mut y = 1; +let r = if (cond) &mut x else &mut y; +*r = *r + 1; +``` + +(2) You want another function to modify your local value. + +```move +let mut x = 0; +modify_ref(&mut x); +``` + +This sort of modification is how you modify structs and vectors! + +```move +let mut v = vector[]; +vector::push_back(&mut v, 100); +assert!(*vector::borrow(&v, 0) == 100, 42); +``` + +For more details, see [Move references](./primitive-types/references.md). + +## Scopes + +Any local declared with `let` is available for any subsequent expression, _within that scope_. +Scopes are declared with expression blocks, `{`...`}`. + +Locals cannot be used outside of the declared scope. + +```move +let x = 0; +{ + let y = 1; +}; +x + y // ERROR! +// ^ unbound local 'y' +``` + +But, locals from an outer scope _can_ be used in a nested scope. + +```move +{ + let x = 0; + { + let y = x + 1; // valid + } +} +``` + +Locals can be mutated in any scope where they are accessible. That mutation survives with the local, +regardless of the scope that performed the mutation. + +```move +let mut x = 0; +x = x + 1; +assert!(x == 1, 42); +{ + x = x + 1; + assert!(x == 2, 42); +}; +assert!(x == 2, 42); +``` + +### Expression Blocks + +An expression block is a series of statements separated by semicolons (`;`). The resulting value of +an expression block is the value of the last expression in the block. + +```move +{ let x = 1; let y = 1; x + y } +``` + +In this example, the result of the block is `x + y`. + +A statement can be either a `let` declaration or an expression. Remember that assignments (`x = e`) +are expressions of type `()`. + +```move +{ let x; let y = 1; x = 1; x + y } +``` + +Function calls are another common expression of type `()`. Function calls that modify data are +commonly used as statements. + +```move +{ let v = vector[]; vector::push_back(&mut v, 1); v } +``` + +This is not just limited to `()` types---any expression can be used as a statement in a sequence! + +```move +{ + let x = 0; + x + 1; // value is discarded + x + 2; // value is discarded + b"hello"; // value is discarded +} +``` + +But! If the expression contains a resource (a value without the `drop` [ability](./abilities.md)), +you will get an error. This is because Move's type system guarantees that any value that is dropped +has the `drop` [ability](./abilities.md). (Ownership must be transferred or the value must be +explicitly destroyed within its declaring module.) + +```move +{ + let x = 0; + Coin { value: x }; // ERROR! +// ^^^^^^^^^^^^^^^^^ unused value without the `drop` ability + x +} +``` + +If a final expression is not present in a block---that is, if there is a trailing semicolon `;`, +there is an implicit [unit `()` value](https://en.wikipedia.org/wiki/Unit_type). Similarly, if the +expression block is empty, there is an implicit unit `()` value. + +Both are equivalent + +```move +{ x = x + 1; 1 / x; } +``` + +```move +{ x = x + 1; 1 / x; () } +``` + +Similarly both are equivalent + +```move +{ } +``` + +```move +{ () } +``` + +An expression block is itself an expression and can be used anyplace an expression is used. (Note: +The body of a function is also an expression block, but the function body cannot be replaced by +another expression.) + +```move +let my_vector: vector> = { + let mut v = vector[]; + vector::push_back(&mut v, b"hello"); + vector::push_back(&mut v, b"goodbye"); + v +}; +``` + +(The type annotation is not needed in this example and only added for clarity.) + +### Shadowing + +If a `let` introduces a local variable with a name already in scope, that previous variable can no +longer be accessed for the rest of this scope. This is called _shadowing_. + +```move +let x = 0; +assert!(x == 0, 42); + +let x = 1; // x is shadowed +assert!(x == 1, 42); +``` + +When a local is shadowed, it does not need to retain the same type as before. + +```move +let x = 0; +assert!(x == 0, 42); + +let x = b"hello"; // x is shadowed +assert!(x == b"hello", 42); +``` + +After a local is shadowed, the value stored in the local still exists, but will no longer be +accessible. This is important to keep in mind with values of types without the +[`drop` ability](./abilities.md), as ownership of the value must be transferred by the end of the +function. + +```move +module 0x42::example { + public struct Coin has store { value: u64 } + + fun unused_coin(): Coin { + let x = Coin { value: 0 }; // ERROR! +// ^ This local still contains a value without the `drop` ability + x.value = 1; + let x = Coin { value: 10 }; + x +// ^ Invalid return + } +} +``` + +When a local is shadowed inside a scope, the shadowing only remains for that scope. The shadowing is +gone once that scope ends. + +```move +let x = 0; +{ + let x = 1; + assert!(x == 1, 42); +}; +assert!(x == 0, 42); +``` + +Remember, locals can change type when they are shadowed. + +```move +let x = 0; +{ + let x = b"hello"; + assert!(x = b"hello", 42); +}; +assert!(x == 0, 42); +``` + +## Move and Copy + +All local variables in Move can be used in two ways, either by `move` or `copy`. If one or the other +is not specified, the Move compiler is able to infer whether a `copy` or a `move` should be used. +This means that in all of the examples above, a `move` or a `copy` would be inserted by the +compiler. A local variable cannot be used without the use of `move` or `copy`. + +`copy` will likely feel the most familiar coming from other programming languages, as it creates a +new copy of the value inside of the variable to use in that expression. With `copy`, the local +variable can be used more than once. + +```move +let x = 0; +let y = copy x + 1; +let z = copy x + 2; +``` + +Any value with the `copy` [ability](./abilities.md) can be copied in this way, and will be copied +implicitly unless a `move` is specified. + +`move` takes the value out of the local variable _without_ copying the data. After a `move` occurs, +the local variable is unavailable, even if the value's type has the `copy` +[ability](./abilities.md). + +```move +let x = 1; +let y = move x + 1; +// ------ Local was moved here +let z = move x + 2; // Error! +// ^^^^^^ Invalid usage of local 'x' +y + z +``` + +### Safety + +Move's type system will prevent a value from being used after it is moved. This is the same safety +check described in [`let` declaration](#let-bindings) that prevents local variables from being used +before it is assigned a value. + + + +### Inference + +As mentioned above, the Move compiler will infer a `copy` or `move` if one is not indicated. The +algorithm for doing so is quite simple: + +- Any value with the `copy` [ability](./abilities.md) is given a `copy`. +- Any reference (both mutable `&mut` and immutable `&`) is given a `copy`. + - Except under special circumstances where it is made a `move` for predictable borrow checker + errors. This will happen once the reference is no longer used. +- Any other value is given a `move`. + +Given the structs + +```move +public struct Foo has copy, drop, store { f: u64 } +public struct Coin has store { value: u64 } +``` + +we have the following example + +```move +let s = b"hello"; +let foo = Foo { f: 0 }; +let coin = Coin { value: 0 }; +let coins = vector[Coin { value: 0 }, Coin { value: 0 }]; + +let s2 = s; // copy +let foo2 = foo; // copy +let coin2 = coin; // move +let coins2 = coin; // move + +let x = 0; +let b = false; +let addr = @0x42; +let x_ref = &x; +let coin_ref = &mut coin2; + +let x2 = x; // copy +let b2 = b; // copy +let addr2 = @0x42; // copy +let x_ref2 = x_ref; // copy +let coin_ref2 = coin_ref; // copy +``` diff --git a/reference/theme b/reference/theme new file mode 120000 index 00000000..1c507275 --- /dev/null +++ b/reference/theme @@ -0,0 +1 @@ +../theme \ No newline at end of file