Move programs can create, delete, and update resources in global storage using the following five instructions:
Operation | Description | Aborts? |
---|---|---|
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 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:
struct R has key { f: u64 }// will not compilefun ret_direct_resource_ref_bad(a: address): &R { borrow_global<R>(a) // error!}// also will not compilefun ret_resource_field_ref_bad(a: address): &u64 { &borrow_global<R>(a).f // error!}
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 operations can be applied to generic resources with both instantiated and uninstantiated generic type parameters:
struct Container<T> has key { t: T }// Publish a Container storing a type T of the caller's choosingfun publish_generic_container<T>(account: &signer, t: T) { move_to<Container<T>>(account, Container { t })}/// Publish a container storing a u64fun publish_instantiated_generic_container(account: &signer, t: u64) { move_to<Container<u64>>(account, Container { t })}
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.
Counter
​The simple Counter
module below exercises each of the five global storage operators. The API exposed by this module allows:
Counter
resource under their accountCounter
exists under any addressCounter
resource under any addressCounter
resource to reset it to zero