Resources
Enables resource-oriented programming on Flow
Resources are types that can only exist in one location at a time and must be used exactly once.
Resources must be created (instantiated) by using the create
keyword.
At the end of a function which has resources (variables, constants, parameters) in scope, the resources must be either moved or destroyed.
They are moved when used as an initial value for a constant or variable, when assigned to a different variable, when passed as an argument to a function, and when returned from a function.
Resources can be explicitly destroyed using the destroy
keyword.
Accessing a field or calling a function of a resource does not move or destroy it.
When the resource is moved, the constant or variable that referred to the resource before the move becomes invalid. An invalid resource cannot be used again.
To make the usage and behaviour of resource types explicit,
the prefix @
must be used in type annotations
of variable or constant declarations, parameters, and return types.
The Move Operator (<-
)
To make moves of resources explicit, the move operator <-
must be used
when the resource is the initial value of a constant or variable,
when it is moved to a different variable,
when it is moved to a function as an argument,
and when it is returned from a function.
// Declare a resource named `SomeResource`, with a variable integer field.
//
pub resource SomeResource {
pub var value: Int
init(value: Int) {
self.value = value
}
}
// Declare a constant with value of resource type `SomeResource`.
//
let a: @SomeResource <- create SomeResource(value: 0)
// *Move* the resource value to a new constant.
//
let b <- a
// Invalid: Cannot use constant `a` anymore as the resource that it referred to
// was moved to constant `b`.
//
a.value
// Constant `b` owns the resource.
//
b.value // equals 0
// Declare a function which accepts a resource.
//
// The parameter has a resource type, so the type annotation must be prefixed with `@`.
//
pub fun use(resource: @SomeResource) {
// ...
}
// Call function `use` and move the resource into it.
//
use(resource: <-b)
// Invalid: Cannot use constant `b` anymore as the resource
// it referred to was moved into function `use`.
//
b.value
A resource object cannot go out of scope and be dynamically lost. The program must either explicitly destroy it or move it to another context.
{
// Declare another, unrelated value of resource type `SomeResource`.
//
let c <- create SomeResource(value: 10)
// Invalid: `c` is not used before the end of the scope, but must be.
// It cannot be lost.
}
// Declare another, unrelated value of resource type `SomeResource`.
//
let d <- create SomeResource(value: 20)
// Destroy the resource referred to by constant `d`.
//
destroy d
// Invalid: Cannot use constant `d` anymore as the resource
// it referred to was destroyed.
//
d.value
To make it explicit that the type is a resource type
and must follow the rules associated with resources,
it must be prefixed with @
in all type annotations,
e.g. for variable declarations, parameters, or return types.
// Declare a constant with an explicit type annotation.
//
// The constant has a resource type, so the type annotation must be prefixed with `@`.
//
let someResource: @SomeResource <- create SomeResource(value: 5)
// Declare a function which consumes a resource and destroys it.
//
// The parameter has a resource type, so the type annotation must be prefixed with `@`.
//
pub fun use(resource: @SomeResource) {
destroy resource
}
// Declare a function which returns a resource.
//
// The return type is a resource type, so the type annotation must be prefixed with `@`.
// The return statement must also use the `<-` operator to make it explicit the resource is moved.
//
pub fun get(): @SomeResource {
let newResource <- create SomeResource()
return <-newResource
}
Resources must be used exactly once.
// Declare a function which consumes a resource but does not use it.
// This function is invalid, because it would cause a loss of the resource.
//
pub fun forgetToUse(resource: @SomeResource) {
// Invalid: The resource parameter `resource` is not used, but must be.
}
// Declare a constant named `res` which has the resource type `SomeResource`.
let res <- create SomeResource()
// Call the function `use` and move the resource `res` into it.
use(resource: <-res)
// Invalid: The resource constant `res` cannot be used again,
// as it was moved in the previous function call.
//
use(resource: <-res)
// Invalid: The resource constant `res` cannot be used again,
// as it was moved in the previous function call.
//
res.value
// Declare a function which has a resource parameter.
// This function is invalid, because it does not always use the resource parameter,
// which would cause a loss of the resource.
//
pub fun sometimesDestroy(resource: @SomeResource, destroyResource: Bool) {
if destroyResource {
destroy resource
}
// Invalid: The resource parameter `resource` is not always used, but must be.
// The destroy statement is not always executed, so at the end of this function
// it might have been destroyed or not.
}
// Declare a function which has a resource parameter.
// This function is valid, as it always uses the resource parameter,
// and does not cause a loss of the resource.
//
pub fun alwaysUse(resource: @SomeResource, destroyResource: Bool) {
if destroyResource {
destroy resource
} else {
use(resource: <-resource)
}
// At the end of the function the resource parameter was definitely used:
// It was either destroyed or moved in the call of function `use`.
}
// Declare a function which has a resource parameter.
// This function is invalid, because it does not always use the resource parameter,
// which would cause a loss of the resource.
//
pub fun returnBeforeDestroy(move: Bool) {
let res <- create SomeResource(value: 1)
if move {
use(resource: <-res)
return
} else {
// Invalid: When this function returns here, the resource variable
// `res` was not used, but must be.
return
}
// Invalid: the resource variable `res` was potentially moved in the
// previous if-statement, and both branches definitely return,
// so this statement is unreachable.
destroy res
}
Resource Variables
Resource variables cannot be assigned to, as that would lead to the loss of the variable's current resource value.
Instead, use a swap statement (<->
) or shift statement (<- target <-
)
to replace the resource variable with another resource.
pub resource R {}
var x <- create R()
var y <- create R()
// Invalid: Cannot assign to resource variable `x`,
// as its current resource would be lost
//
x <- y
// Instead, use a swap statement.
//
var replacement <- create R()
x <-> replacement
// `x` is the new resource.
// `replacement` is the old resource.
// Or use the shift statement (`<- target <-`)
// This statement moves the resource out of `x` and into `oldX`,
// and at the same time assigns `x` with the new value on the right-hand side.
let oldX <- x <- create R()
// oldX still needs to be explicitly handled after this statement
destroy oldX
Resource Destructors
Resource may have a destructor, which is executed when the resource is destroyed.
Destructors have no parameters and no return value and are declared using the destroy
name.
A resource may have only one destructor.
var destructorCalled = false
pub resource Resource {
// Declare a destructor for the resource, which is executed
// when the resource is destroyed.
//
destroy() {
destructorCalled = true
}
}
let res <- create Resource()
destroy res
// `destructorCalled` is `true`
Nested Resources
Fields in composite types behave differently when they have a resource type.
If a resource type has fields that have a resource type, it must declare a destructor, which must invalidate all resource fields, i.e. move or destroy them.
pub resource Child {
let name: String
init(name: String)
self.name = name
}
}
// Declare a resource with a resource field named `child`.
// The resource *must* declare a destructor
// and the destructor *must* invalidate the resource field.
//
pub resource Parent {
let name: String
var child: @Child
init(name: String, child: @Child) {
self.name = name
self.child <- child
}
// Declare a destructor which invalidates the resource field
// `child` by destroying it.
//
destroy() {
destroy self.child
}
}
Accessing a field or calling function on a resource field is valid, however moving a resource out of a variable resource field is not allowed. Instead, use a swap statement to replace the resource with another resource.
let child <- create Child(name: "Child 1")
let parent <- create Parent(name: "Parent", child: <-child)
child.name // is "Child 1"
parent.child.name // is "Child 1"
// Invalid: Cannot move resource out of variable resource field.
let childAgain <- parent.child
// Instead, use a swap statement.
//
var otherChild <- create Child(name: "Child 2")
parent.child <-> otherChild
// `parent.child` is the second child, Child 2.
// `otherChild` is the first child, Child 1.
Resources in Closures
Resources can not be captured in closures, as that could potentially result in duplications.
resource R {}
// Invalid: Declare a function which returns a closure which refers to
// the resource parameter `resource`. Each call to the returned function
// would return the resource, which should not be possible.
//
fun makeCloner(resource: @R): ((): @R) {
return fun (): @R {
return <-resource
}
}
let test = makeCloner(resource: <-create R())
Resources in Arrays and Dictionaries
Arrays and dictionaries behave differently when they contain resources: It is not allowed to index into an array to read an element at a certain index or assign to it, or index into a dictionary to read a value for a certain key or set a value for the key.
Instead, use a swap statement (<->
) or shift statement (<- target <-
)
to replace the accessed resource with another resource.
resource R {}
// Declare a constant for an array of resources.
// Create two resources and move them into the array.
// `resources` has type `@[R]`
//
let resources <- [
<-create R(),
<-create R()
]
// Invalid: Reading an element from a resource array is not allowed.
//
let firstResource <- resources[0]
// Invalid: Setting an element in a resource array is not allowed,
// as it would result in the loss of the current value.
//
resources[0] <- create R()
// Instead, when attempting to either read an element or update an element
// in a resource array, use a swap statement with a variable to replace
// the accessed element.
//
var res <- create R()
resources[0] <-> res
// `resources[0]` now contains the new resource.
// `res` now contains the old resource.
// Use the shift statement to move the new resource into
// the array at the same time that the old resource is being moved out
let oldRes <- resources[0] <- create R()
// The old object still needs to be handled
destroy oldRes
The same applies to dictionaries.
// Declare a constant for a dictionary of resources.
// Create two resources and move them into the dictionary.
// `resources` has type `@{String: R}`
//
let resources <- {
"r1": <-create R(),
"r2": <-create R()
}
// Invalid: Reading an element from a resource dictionary is not allowed.
// It's not obvious that an access like this would have to remove
// the key from the dictionary.
//
let firstResource <- resources["r1"]
// Instead, make the removal explicit by using the `remove` function.
let firstResource <- resources.remove(key: "r1")
// Invalid: Setting an element in a resource dictionary is not allowed,
// as it would result in the loss of the current value.
//
resources["r1"] <- create R()
// Instead, when attempting to either read an element or update an element
// in a resource dictionary, use a swap statement with a variable to replace
// the accessed element.
//
var res <- create R()
resources["r1"] <-> res
// `resources["r1"]` now contains the new resource.
// `res` now contains the old resource.
// Use the shift statement to move the new resource into
// the dictionary at the same time that the old resource is being moved out
let oldRes <- resources["r2"] <- create R()
// The old object still needs to be handled
destroy oldRes
Resources cannot be moved into arrays and dictionaries multiple times, as that would cause a duplication.
let resource <- create R()
// Invalid: The resource variable `resource` can only be moved into the array once.
//
let resources <- [
<-resource,
<-resource
]
let resource <- create R()
// Invalid: The resource variable `resource` can only be moved into the dictionary once.
let resources <- {
"res1": <-resource,
"res2": <-resource
}
Resource arrays and dictionaries can be destroyed.
let resources <- [
<-create R(),
<-create R()
]
destroy resources
let resources <- {
"r1": <-create R(),
"r2": <-create R()
}
destroy resources
The variable array functions like append
, insert
, and remove
behave like for non-resource arrays.
Note however, that the result of the remove
functions must be used.
let resources <- [<-create R()]
// `resources.length` is `1`
resources.append(<-create R())
// `resources.length` is `2`
let first <- resource.remove(at: 0)
// `resources.length` is `1`
destroy first
resources.insert(at: 0, <-create R())
// `resources.length` is `2`
// Invalid: The statement ignores the result of the call to `remove`,
// which would result in a loss.
resource.remove(at: 0)
destroy resources
The variable array function contains
is not available, as it is impossible:
If the resource can be passed to the contains
function,
it is by definition not in the array.
The variable array function concat
is not available,
as it would result in the duplication of resources.
The dictionary functions like insert
and remove
behave like for non-resource dictionaries.
Note however, that the result of these functions must be used.
let resources <- {"r1": <-create R()}
// `resources.length` is `1`
let first <- resource.remove(key: "r1")
// `resources.length` is `0`
destroy first
let old <- resources.insert(key: "r1", <-create R())
// `old` is nil, as there was no value for the key "r1"
// `resources.length` is `1`
let old2 <- resources.insert(key: "r1", <-create R())
// `old2` is the old value for the key "r1"
// `resources.length` is `1`
destroy old
destroy old2
destroy resources
Resource Identifier
Resources have an implicit unique identifier associated with them,
implemented by a predeclared public field let uuid: UInt64
on each resource.
This identifier will be automatically set when the resource is created, before the resource's initializer is called (i.e. the identifier can be used in the initializer), and will be unique even after the resource is destroyed, i.e. no two resources will ever have the same identifier.
// Declare a resource without any fields.
resource R {}
// Create two resources
let r1 <- create R()
let r2 <- create R()
// Get each resource's unique identifier
let id1 = r1.uuid
let id2 = r2.uuid
// Destroy the first resource
destroy r1
// Create a third resource
let r3 <- create R()
let id3 = r3.uuid
id1 != id2 // true
id2 != id3 // true
id3 != id1 // true
Do not rely on or assume any particular behaviour in Cadence programs.
Unbound References / Nulls
There is no support for null
.
Inheritance and Abstract Types
There is no support for inheritance. Inheritance is a feature common in other programming languages, that allows including the fields and functions of one type in another type.
Instead, follow the "composition over inheritance" principle, the idea of composing functionality from multiple individual parts, rather than building an inheritance tree.
Furthermore, there is also no support for abstract types. An abstract type is a feature common in other programming languages, that prevents creating values of the type and only allows the creation of values of a subtype. In addition, abstract types may declare functions, but omit the implementation of them and instead require subtypes to implement them.
Instead, consider using interfaces.