From 462ae2ccae80be9f556150ac2ba570a76195295f Mon Sep 17 00:00:00 2001
From: wrwg There are also no restrictions for what the argument types a public function can take and its return type. Entry functions can take primitive types, String, and vector arguments but cannot take Structs (e.g. Option). They also
+must not have any return values. Function names can start with letters After the name, functions can have type parameters Inline functions are functions which are expanded at caller side instead
+of compiled into Move bytecode. This allows to safe gas and may lead
+to faster execution. For example, one can define an inline function Now, when call Inline functions support function parameters. This allows
+to define higher-order functions in Move which can comprehend common
+programming patterns. As inline functions are expanded at compilation time,
+this feature of function parameters can be supported without direct
+support for it in Move bytecode. Consider the following function (from the Here, In general, only lambda expressions can be passed to function parameters.
+Similar as the inline function itself, those lambdas are expanded at caller
+side. Notice that a lambda can access variables in the context, as in the
+example the variable There are also no restrictions for what the argument types a public function can take and its return type. Entry functions can take primitive types, String, and vector arguments but cannot take Structs (e.g. Option). They also
+must not have any return values. Function names can start with letters After the name, functions can have type parameters Inline functions are functions which are expanded at caller side instead
+of compiled into Move bytecode. This allows to safe gas and may lead
+to faster execution. For example, one can define an inline function Now, when call Inline functions support function parameters. This allows
+to define higher-order functions in Move which can comprehend common
+programming patterns. As inline functions are expanded at compilation time,
+this feature of function parameters can be supported without direct
+support for it in Move bytecode. Consider the following function (from the Here, In general, only lambda expressions can be passed to function parameters.
+Similar as the inline function itself, those lambdas are expanded at caller
+side. Notice that a lambda can access variables in the context, as in the
+example the variable A struct is a user-defined data structure containing typed fields. Structs can store any
non-reference type, including other structs.Example: conditional
copy
-struct NoAbilities {}
+
struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
@@ -292,7 +292,7 @@
Example: conditional
-drop
struct NoAbilities {}
+
struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
@@ -318,13 +318,13 @@
Example: conditional
-store
struct Cup<T> has copy, drop, store { item: T }
+
struct Cup<T> has copy, drop, store { item: T }
// 'MyInnerResource' is declared with 'store' so all fields need 'store'
struct MyInnerResource has store {
@@ -340,7 +340,7 @@
Example: conditional
-key
struct NoAbilities {}
+
struct NoAbilities {}
struct MyResource<T> has key { f: T }
fun valid(account: &signer) acquires MyResource {
diff --git a/coding-conventions.html b/coding-conventions.html
index 505ddf7707..38c9ee5215 100644
--- a/coding-conventions.html
+++ b/coding-conventions.html
@@ -159,7 +159,7 @@
Naming
E
if they represent error codes (e.g., EIndexOutOfBounds
) and upper snake case if they represent a non-error value (e.g., MIN_STAKE
).T
or Element
for the Vector generic type parameter. Most of the time the "main" type in a module should be the same name as the module e.g., option::Option
, fixed_point32::FixedPoint32
.Option.move
.option.move
.pub
+address 0x42 {
module m {
public fun foo(): u64 { 0 }
@@ -301,12 +302,13 @@
entry
Name
a
to z
or 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
.fun FOO() {}
fun bar_42() {}
fun _bAZ19() {}
- ^^^^^^ Invalid function name '_bAZ19'. Function names cannot start with '_'
Type Parameters
ret
+fun foo() { return }
fun foo() { return () }
Inline Functions
+
+inline fun percent(x: u64, y: u64):u64 { x * 100 / y }
+
percent(2, 200)
the compiler will inline the function
+definition as if the user has written 2 * 100 / 200
.Function Parameters
+vector
module):
+/// Fold the function over the elements. For example, `fold(vector[1,2,3], 0, f)` will execute
+/// `f(f(f(0, 1), 2), 3)`
+public inline fun fold<Accumulator, Element>(
+ v: vector<Element>,
+ init: Accumulator,
+ f: |Accumulator,Element|Accumulator
+): Accumulator {
+ let accu = init;
+ foreach(v, |elem| accu = g(accu, elem));
+ accu
+}
+
foreach
is itself an inline function.accu
.pub
+address 0x42 {
module m {
public fun foo(): u64 { 0 }
@@ -2076,12 +2077,13 @@
entry
Name
a
to z
or 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
.fun FOO() {}
fun bar_42() {}
fun _bAZ19() {}
- ^^^^^^ Invalid function name '_bAZ19'. Function names cannot start with '_'
Type Parameters
-ret
+fun foo() { return }
fun foo() { return () }
Inline Functions
+
+inline fun percent(x: u64, y: u64):u64 { x * 100 / y }
+
percent(2, 200)
the compiler will inline the function
+definition as if the user has written 2 * 100 / 200
.Function Parameters
+vector
module):
+/// Fold the function over the elements. For example, `fold(vector[1,2,3], 0, f)` will execute
+/// `f(f(f(0, 1), 2), 3)`
+public inline fun fold<Accumulator, Element>(
+ v: vector<Element>,
+ init: Accumulator,
+ f: |Accumulator,Element|Accumulator
+): Accumulator {
+ let accu = init;
+ foreach(v, |elem| accu = g(accu, elem));
+ accu
+}
+
foreach
is itself an inline function.accu
.Structs and Resources
Example: conditional
copy
struct NoAbilities {}
+
struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
@@ -3344,7 +3378,7 @@
Example: conditional
-drop
struct NoAbilities {}
+
struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
@@ -3370,13 +3404,13 @@
Example: conditional
-store
struct Cup<T> has copy, drop, store { item: T }
+
struct Cup<T> has copy, drop, store { item: T }
// 'MyInnerResource' is declared with 'store' so all fields need 'store'
struct MyInnerResource has store {
@@ -3392,7 +3426,7 @@
Example: conditional
-key
struct NoAbilities {}
+
struct NoAbilities {}
struct MyResource<T> has key { f: T }
fun valid(account: &signer) acquires MyResource {
@@ -4144,7 +4178,7 @@
Running
command.
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, and its default value is set to 5000 instructions. 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).
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. These can be found using the the help flag:
+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. These can be found using the help flag:
$ move -h
const INVALID_STATE: u8 = 1;
A specific account address was required to perform an operation, but a different address from what was expected was encounterd.
+A specific account address was required to perform an operation, but a different address from what was expected was encountered.
const REQUIRES_ADDRESS: u8 = 2;
E
if they represent error codes (e.g., EIndexOutOfBounds
) and upper snake case if they represent a non-error value (e.g., MIN_STAKE
).T
or Element
for the Vector generic type parameter. Most of the time the "main" type in a module should be the same name as the module e.g., option::Option
, fixed_point32::FixedPoint32
.Option.move
.option.move
.)], if it then fails with a different abort code or with a non-abort error the test will fail. Only functions that have the #[test] annotation can also be annotated as an #[expected_failure]. #[test]\n#[expected_failure]\npublic fun this_test_will_abort_and_pass() { abort 1 } #[test]\n#[expected_failure]\npublic fun test_will_error_and_pass() { 1/0; } #[test]\n#[expected_failure(abort_code = 0)]\npublic fun test_will_error_and_fail() { 1/0; } #[test, expected_failure] // Can have multiple in one attribute. This test will pass.\npublic fun this_other_test_will_abort_and_pass() { abort 1 } With arguments, a test annotation takes the form #[test( = , ..., = )]. If a function is annotated in such a manner, the function's parameters must be a permutation of the parameters , ..., , i.e., the order of these parameters as they occur in the function and their order in the test annotation do not have to be the same, but they must be able to be matched up with each other by name. Only parameters with a type of signer are supported as test parameters. If a non-signer parameter is supplied, the test will result in an error when run. #[test(arg = @0xC0FFEE)] // OK\nfun this_is_correct_now(arg: signer) { ... } #[test(wrong_arg_name = @0xC0FFEE)] // Not correct: arg name doesn't match\nfun this_is_incorrect(arg: signer) { ... } #[test(a = @0xC0FFEE, b = @0xCAFE)] // OK. We support multiple signer arguments, but you must always provide a value for that argument\nfun this_works(a: signer, b: signer) { ... } // somewhere a named address is declared\n#[test_only] // test-only named addresses are supported\naddress TEST_NAMED_ADDR = @0x1;\n...\n#[test(arg = @TEST_NAMED_ADDR)] // Named addresses are supported!\nfun this_is_correct_now(arg: signer) { ... } An expected failure annotation can also take the form #[expected_failure(abort_code = )]. If a test function is annotated in such a way, the test must abort with an abort code equal to . Any other failure or abort code will result in a test failure. #[test, expected_failure(abort_code = 1)] // This test will fail\nfun this_test_should_abort_and_fail() { abort 0 } #[test]\n#[expected_failure(abort_code = 0)] // This test will pass\nfun this_test_should_abort_and_pass_too() { abort 0 } A module and any of its members can be declared as test only. In such a case 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 uses of a #[test_only] module will raise an error during compilation. #[test_only] // test only attributes can be attached to modules\nmodule abc { ... } #[test_only] // test only attributes can be attached to named addresses\naddress ADDR = @0x1; #[test_only] // .. to uses\nuse 0x1::some_other_module; #[test_only] // .. to structs\nstruct SomeStruct { ... } #[test_only] // .. and functions. Can only be called from test code, but not a test\nfun test_only_function(...) { ... }","breadcrumbs":"Unit Tests » Testing Annotations: Their Meaning and Usage","id":"187","title":"Testing Annotations: Their Meaning and Usage"},"188":{"body":"Unit tests for a Move package can be run with the move test command . 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, and its default value is set to 5000 instructions. 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). 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. These can be found using the the help flag: $ move -h","breadcrumbs":"Unit Tests » Running Unit Tests","id":"188","title":"Running Unit Tests"},"189":{"body":"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: $ move new TestExample; cd TestExample Next add the following to the Move.toml: [dependencies]\nMoveStdlib = { git = \"https://github.com/diem/diem.git\", subdir=\"language/move-stdlib\", rev = \"56ab033cc403b489e891424a629e76f643d4fb6b\", addr_subst = { \"std\" = \"0x1\" } } Next add the following module under the sources directory: // filename: sources/my_module.move\nmodule 0x1::my_module { struct MyCoin has key { value: u64 } public fun make_sure_non_zero_coin(coin: MyCoin): MyCoin { assert!(coin.value > 0, 0); coin } public fun has_coin(addr: address): bool { exists(addr) } #[test] fun make_sure_non_zero_coin_passes() { let coin = MyCoin { value: 1 }; let MyCoin { value: _ } = make_sure_non_zero_coin(coin); } #[test] // Or #[expected_failure] if we don't care about the abort code #[expected_failure(abort_code = 0)] fun make_sure_zero_coin_fails() { let coin = MyCoin { value: 0 }; let MyCoin { value: _ } = make_sure_non_zero_coin(coin); } #[test_only] // test only helper function fun publish_coin(account: &signer) { move_to(account, MyCoin { value: 1 }) } #[test(a = @0x1, b = @0x2)] fun test_has_coin(a: signer, b: signer) { publish_coin(&a); publish_coin(&b); assert!(has_coin(@0x1), 0); assert!(has_coin(@0x2), 1); assert!(!has_coin(@0x3), 1); }\n}","breadcrumbs":"Unit Tests » Example","id":"189","title":"Example"},"19":{"body":"Like all types with drop in Move, all integer types support the \"equal\" and \"not equal\" operations. Both arguments need to be of the same type. If you need to compare integers of different types, you will need to cast one of them first. Equality operations do not abort. Syntax Operation == equal != not equal For more details see the section on equality","breadcrumbs":"Integers » Equality","id":"19","title":"Equality"},"190":{"body":"You can then run these tests with the move test command: $ move test\nBUILDING MoveStdlib\nBUILDING TestExample\nRunning Move unit tests\n[ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes\n[ PASS ] 0x1::my_module::make_sure_zero_coin_fails\n[ PASS ] 0x1::my_module::test_has_coin\nTest result: OK. Total tests: 3; passed: 3; failed: 0","breadcrumbs":"Unit Tests » Running Tests","id":"190","title":"Running Tests"},"191":{"body":"-f or --filter This will only run tests whose fully qualified name contains . For example if we wanted to only run tests with \"zero_coin\" in their name: $ move test -f zero_coin\nCACHED MoveStdlib\nBUILDING TestExample\nRunning Move unit tests\n[ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes\n[ PASS ] 0x1::my_module::make_sure_zero_coin_fails\nTest result: OK. Total tests: 2; passed: 2; failed: 0 -i or --gas_used This bounds the amount of gas that can be consumed for any one test to : $ move test -i 0\nCACHED MoveStdlib\nBUILDING TestExample\nRunning Move unit tests\n[ TIMEOUT ] 0x1::my_module::make_sure_non_zero_coin_passes\n[ TIMEOUT ] 0x1::my_module::make_sure_zero_coin_fails\n[ TIMEOUT ] 0x1::my_module::test_has_coin Test failures: Failures in 0x1::my_module: ┌── make_sure_non_zero_coin_passes ──────\n│ Test timed out\n└────────────────── ┌── make_sure_zero_coin_fails ──────\n│ Test timed out\n└────────────────── ┌── test_has_coin ──────\n│ Test timed out\n└────────────────── 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. For example, if we wanted to see the statistics for the tests in the example above: $ move test -s\nCACHED MoveStdlib\nBUILDING TestExample\nRunning Move unit tests\n[ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes\n[ PASS ] 0x1::my_module::make_sure_zero_coin_fails\n[ PASS ] 0x1::my_module::test_has_coin Test Statistics: ┌────────────────────────────────────────────────┬────────────┬───────────────────────────┐\n│ Test Name │ Time │ Gas Used │\n├────────────────────────────────────────────────┼────────────┼───────────────────────────┤\n│ 0x1::my_module::make_sure_non_zero_coin_passes │ 0.009 │ 1 │\n├────────────────────────────────────────────────┼────────────┼───────────────────────────┤\n│ 0x1::my_module::make_sure_zero_coin_fails │ 0.008 │ 1 │\n├────────────────────────────────────────────────┼────────────┼───────────────────────────┤\n│ 0x1::my_module::test_has_coin │ 0.008 │ 1 │\n└────────────────────────────────────────────────┴────────────┴───────────────────────────┘ Test result: OK. Total tests: 3; passed: 3; failed: 0 -g or --state-on-error These flags will print the global state for any test failures. e.g., if we added the following (failing) test to the my_module example: module 0x1::my_module { ... #[test(a = @0x1)] fun test_has_coin_bad(a: signer) { publish_coin(&a); assert!(has_coin(@0x1), 0); assert!(has_coin(@0x2), 1); }\n} we would get the following output when running the tests: $ move test -g\nCACHED MoveStdlib\nBUILDING TestExample\nRunning Move unit tests\n[ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes\n[ PASS ] 0x1::my_module::make_sure_zero_coin_fails\n[ PASS ] 0x1::my_module::test_has_coin\n[ FAIL ] 0x1::my_module::test_has_coin_bad Test failures: Failures in 0x1::my_module: ┌── test_has_coin_bad ──────\n│ error[E11001]: test failure\n│ ┌─ /home/tzakian/TestExample/sources/my_module.move:47:10\n│ │\n│ 44 │ fun test_has_coin_bad(a: signer) {\n│ │ ----------------- In this function in 0x1::my_module\n│ ·\n│ 47 │ assert!(has_coin(@0x2), 1);\n│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ Test was not expected to abort but it aborted with 1 here\n│\n│\n│ ────── Storage state at point of failure ──────\n│ 0x1:\n│ => key 0x1::my_module::MyCoin {\n│ value: 1\n│ }\n│\n└────────────────── Test result: FAILED. Total tests: 4; passed: 3; failed: 1","breadcrumbs":"Unit Tests » Using Test Flags","id":"191","title":"Using Test Flags"},"192":{"body":"The purpose of Move programs is to read from and write to tree-shaped persistent global storage. Programs cannot access the filesystem, network, or any other data outside of this tree. In pseudocode, the global storage looks something like: struct GlobalStorage { resources: Map<(address, ResourceType), ResourceValue> modules: Map<(address, ModuleName), ModuleBytecode>\n} Structurally, global storage is a forest consisting of trees rooted at an account address . Each address can store both resource data values and module code values. As the pseudocode above indicates, each address can store at most one resource value of a given type and at most one module with a given name.","breadcrumbs":"Global Storage Structure » Global Storage - Structure","id":"192","title":"Global Storage - Structure"},"193":{"body":"Move programs can create, delete, and update resources in global storage using the following five instructions: Operation Description Aborts? move_to