Global Storage - Operators
Global Storage - Operators
Move programs can create, delete, and update resources in global storage using the following five instructions:
move_to<T>(&signer,T)
Publish T
under signer.address
If signer.address
already holds a T
move_from<T>(address): T
Remove T
from address
and return it
If address
does not hold a T
borrow_global_mut<T>(address): &mut T
Return a mutable reference to the T
stored under address
If address
does not hold a T
borrow_global<T>(address): &T
Return an immutable reference to the T
stored under address
If address
does not hold a T
exists<T>(address): bool
Return true
if a T
is stored under address
Never
Each of these instructions is parameterized by a type T
with the key
ability. However, each type T
must be declared in the current module. This ensures that a resource can only be manipulated via the API exposed by its defining module. The instructions also take either an address
or &signer
representing the account address where the resource of type T
is stored.
References to resources
References to global resources returned by borrow_global
or borrow_global_mut
mostly behave like references to local storage: they can be extended, read, and written using ordinary reference operators and passed as arguments to other function. However, there is one important difference between local and global references: a function cannot return a reference that points into global storage. For example, these two functions will each fail to compile:
Move must enforce this restriction to guarantee absence of dangling references to global storage. This section contains much more detail for the interested reader.
Global storage operators with generics
Global storage operations can be applied to generic resources with both instantiated and uninstantiated generic type parameters:
The ability to index into global storage via a type parameter chosen at runtime is a powerful Move feature known as storage polymorphism. For more on the design patterns enabled by this feature, see Move generics.
Example: Counter
Counter
The simple Counter
module below exercises each of the five global storage operators. The API exposed by this module allows:
Anyone to publish a
Counter
resource under their accountAnyone to check if a
Counter
exists under any addressAnyone to read or increment the value of a
Counter
resource under any addressAn account that stores a
Counter
resource to reset it to zeroAn account that stores a
Counter
resource to remove and delete it
Annotating functions with acquires
acquires
In the counter
example, you might have noticed that the get_count
, increment
, reset
, and delete
functions are annotated with acquires Counter
. A Move function m::f
must be annotated with acquires T
if and only if:
The body of
m::f
contains amove_from<T>
,borrow_global_mut<T>
, orborrow_global<T>
instruction, orThe body of
m::f
invokes a functionm::g
declared in the same module that is annotated withacquires
For example, the following function inside Counter
would need an acquires
annotation:
However, the same function outside Counter
would not need an annotation:
If a function touches multiple resources, it needs multiple acquires
:
The acquires
annotation does not take generic type parameters into account:
Finally: redundant acquires
are not allowed. Adding this function inside Counter
will result in a compilation error:
For more information on acquires
, see Move functions.
Reference Safety For Global Resources
Move prohibits returning global references and requires the acquires
annotation to prevent dangling references. This allows Move to live up to its promise of static reference safety (i.e., no dangling references, no null
or nil
dereferences) for all reference types.
This example illustrates how the Move type system uses acquires
to prevent a dangling reference:
In this code, line 6 acquires a reference to the T
stored at address a
in global storage. The callee remove_t
then removes the value, which makes t_ref
a dangling reference.
Fortunately, this cannot happen because the type system will reject this program. The acquires
annotation on remove_t
lets the type system know that line 7 is dangerous, without having to recheck or introspect the body of remove_t
separately!
The restriction on returning global references prevents a similar, but even more insidious problem:
Line 16 acquires a reference to a global resource m1::T
, then line 17 removes that same resource, which makes t_ref
dangle. In this case, acquires
annotations do not help us because the borrow_then_remove_bad
function is outside the m1
module that declares T
(recall that acquires
annotations can only be used for resources declared in the current module). Instead, the type system avoids this problem by preventing the return of a global reference at line 6.
Fancier type systems that would allow returning global references without sacrificing reference safety are possible, and we may consider them in future iterations of Move. We chose the current design because it strikes a good balance between being expressive, annotation burden, and type system complexity.
Last updated