You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: apps/nextra/pages/en/build/smart-contracts/book/functions.mdx
+43-21
Original file line number
Diff line number
Diff line change
@@ -817,16 +817,19 @@ let f: |u64|bool has copy+drop+store = is_even;
817
817
let g: |u64|bool has copy+drop = is_odd;
818
818
```
819
819
820
-
A public function is required to build a storable function value because it needs to be guaranteed that the underlying function persists so the function value can be safely restored from storage at any point in the future. However, code upgrade may change the underlying implementation of the function, while its signature is persistent.
820
+
A _persistent_ function is required to build a storable function value because it needs to be guaranteed that the underlying function exists and can be safely restored from storage at any point in the future. However, code upgrade may change the underlying implementation of the function, while its signature is persistent.
821
821
822
-
If a function should storable which is non-public, an attribute can be used to mark this function as persistent, with the same upgrade behavior than public functions. This avoids exposing such a function outside of a package which can be a security risk:
822
+
While `public` and `entry` functions are persistent by default, a none-public function needs to be marked with the attribute `#[persistent]` to become storable:
823
823
824
824
```move
825
825
#[persistent] fun is_odd(x: u64): bool { x % 2 == 1 }
826
826
...
827
827
let g: |u64|bool has copy+drop+store = is_odd;
828
828
```
829
829
830
+
Using the `#[persistent]` attribute is preferred if the only objective is to make a function storable, avoiding security implications with public or entry visibility.
831
+
832
+
830
833
### Lambda Expressions and Closures
831
834
832
835
Function values can be denoted by _lambda expressions_ (as also available as parameters for [inline functions](#function-parameters-and-lambda-expressions)). Lambda expressions can capture context variables _by value_: those values are moved (or copied) into a _closure_, from where they are produced when the function is evaluated. Examples:
@@ -839,16 +842,9 @@ let add = |y| { let S(x) = s; x + y }; // s will be moved into the closure
839
842
assert!(add(2) == 3)
840
843
```
841
844
842
-
Notice it is not possible to capture reference values at this point of time in Move. Similarly, it is not possible to mutate any locals in the context of a lambda. Specifically, the following pattern as known from lambdas in inline functions, is not supported:
843
-
844
-
```move
845
-
let x = 0;
846
-
collection.for_each(|e| x += e) // DOES NOT COMPILE
847
-
```
848
-
849
-
The type of the closure constructed by a lambda expression is inferred from the expression in the usual way (for example, the type of `add` in the example above is inferred as `|u64|u64`). The abilities of this function type are derived as follows. By default, the function underlying a closure is a private function, so the function itself is `copy+drop` (and not `store`). This is intersected with the abilities of all the captured context variables.
845
+
Closures with captured values are lexicographical ordered using first the name of the underlying function (which maybe generated from lambda lifting), and then the captured values.
850
846
851
-
A closure can have the `store` ability under the condition that the underlying function is persistent. This is the case if the lambda denoting the closure is based on solely delaying some of the parameters of an underlying persistent function. Example:
847
+
The type of the closure constructed by a lambda expression is inferred from the expression (for example, the type of `add` in the example above is inferred as `|u64|u64`). The abilities of this function type are derived as follows. By default, the function underlying a closure is a private function, so the function itself is `copy+drop` (and not `store`). This is intersected with the abilities of all the captured context variables. However, A closure created from a lambda can have the `store` ability under the condition that the underlying function is persistent. This is the case if the lambda denoting the closure is based on solely delaying some of the parameters of an underlying persistent function. Example:
852
848
853
849
```move
854
850
#[persistent] fun add(x: u64, y: u64) { x + y }
@@ -860,15 +856,36 @@ let f: |u64|u64 has copy+drop+store = |y| add(y, x)
860
856
861
857
For reference, this mechanism is also called 'currying' of `add` in functional programming languages.
862
858
859
+
Notice it is not possible to _capture_ reference values at this point of time in Move. Thus, the following code does not compile:
860
+
861
+
```move
862
+
let x = &22;
863
+
let f = |y| add(*x, y) // DOES NOT COMPILE
864
+
```
865
+
866
+
Related, it is not possible to mutate any locals in the context of a lambda. Specifically, the following pattern as known from lambdas with inline functions, is not supported:
867
+
868
+
```move
869
+
let x = 0;
870
+
collection.for_each(|e| x += e) // DOES NOT COMPILE
871
+
```
872
+
873
+
However, the actual parameters of lambdas can be references, only captured values are restricted. For example:
874
+
875
+
```move
876
+
let x = 22;
877
+
let f : |&u64|u64 = |y| add(x, *y)
878
+
```
879
+
863
880
### Reentrancy Check
864
881
865
-
Via dynamic dispatch of function values, reentrancy of modules in a chain of function calls is possible. If module `m1`uses module `m2`, and `m1` calls `m2::f` passing a function value to it, this function value can callback into `m1`. This situation is called _reentrancy_, and is not possible in Move without function values, since the module usage relation is acyclic.
882
+
Via dynamic dispatch of function values, reentrancy of modules in a chain of function calls is possible. If module `m1` uses module `m2`, and `m1` calls `m2::f` passing a function value to it, this function value can callback into `m1`. This situation is called _reentrancy_, and is not possible in Move without function values, since the module usage relation is acyclic.
866
883
867
-
The Move VM dynamically detects reentrancy for a module, and _locks_ all resources declared in this module from being accessed. Thus during reentrancy, calling the functions `borrow_global`, `borrow_global_mut`, and `move_from` lead to an abort. Here is an example:
884
+
The Move VM dynamically detects reentrancy for a module, and _locks_ all resources declared in this module from being accessed. Thus during reentrancy, calling resource operations like `&R[addr]`, `&mut R[addr]`, and `move_from` lead to an abort. Here is an example:
868
885
869
886
```move
870
-
module caller {
871
-
use addr::callee;
887
+
module 0x42::caller {
888
+
use 0x42::callee;
872
889
struct R{ count: u64 } has key;
873
890
fun calling() acquires R {
874
891
let r = &mut R[@addr];
@@ -881,7 +898,7 @@ module caller {
881
898
fun do_something(r: &mut R) { .. }
882
899
}
883
900
884
-
module callee {
901
+
module 0x42::callee {
885
902
fun call_me<T(x: &mut T, action: |&mut T|) {
886
903
action(x)
887
904
}
@@ -890,16 +907,21 @@ module callee {
890
907
891
908
Notice that dispatching a function value to a concrete function in the same module is also considered to be reentrancy. If the function `callee::call_me` would be moved into the module `caller`, the same semantics is in effect.
892
909
893
-
The default reentrancy check ensures consistency of Move's reference semantics and suppresses side-effects for the resources in a reentered module. But it does not protect from side-effects on other state of the app. For this purposes, the `#[module_lock]` attribute can be attached to a function. In a calling context where this attribute is active, reentrancy will be fully suppressed, causing an abort at the moment a module is reentered. In the example below, the state effected is owned by another module `account` instead of the module `caller` for which the can potentially happen via the `notify` callback:
910
+
The default reentrancy check ensures consistency of Move's reference semantics and suppresses side effects of reentrancy for the resources owned by the re-entered module. However, re-entered code is allowed to still access resource state managed by modules outside the reentrancy path. Such state accesses can be considered bad design, but they exist.
911
+
For these purposes, the `#[module_lock]` attribute can be attached to a function:
894
912
895
913
```move
896
-
module account { ... }
897
-
module caller {
898
-
#[module_lock] // without this lock, the notify call could withdraw unlimited amount
914
+
module 0x42::account { ... }
915
+
module 0x42::caller {
916
+
#[module_lock] // without this lock, the notify call could withdraw more than intended.
899
917
fun transfer(from: address, to: address, amount: u64, notify: |u64|) {
918
+
// Oops. This should be really differently designed, using `Coin` type and moving it.
notify(amount); // attempt to reentrer `transfer` is blocked
921
+
notify(amount); // attempt to re-entrer `transfer` is blocked
902
922
account::withtdraw(from, amout);
903
923
}
904
924
}
905
925
```
926
+
927
+
While a function with this attribute is running, all calls reentering any module will lead to an abort, given a stronger protection. The attribute `#[module_lock]` restriction is not the default behavior since it is too strong for typical patterns of higher-order programming. For example, `collection.find(|x| cond(x))` will lead to a reentrancy of the module which contains this expression, from the module which defines the collection type.
Copy file name to clipboardexpand all lines: apps/nextra/pages/en/build/smart-contracts/book/move-2.mdx
+1-1
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ The Move 2 language releases are described on this page. The reference documenta
6
6
7
7
The Move 2.2 language release adds the following features to Move:
8
8
9
-
-**Acquires Optional**: the`acquires` annotation on function declarations can be omitted, to be inferred by the compiler
9
+
-**Optional Acquires**: The`acquires` annotation on function declarations can be omitted, to be inferred by the compiler.
10
10
-**Function Values**: Move now supports function values, which can be passed around as parameters and stored in resources. See the [reference doc here](functions.mdx#function-values).
0 commit comments