Skip to main content
Version: Next

References

It is possible to create references to objects, i.e. resources or structures. A reference can be used to access fields and call functions on the referenced object.

References are copied, i.e. they are value types.

References have the type &T, where T is the type of the referenced object.

References are created using the & operator. The reference type must be explicitly provided, for example through a type annotation on a variable declaration, or a type assertion using the as operator.


_17
let hello = "Hello"
_17
_17
// Create a reference to the `String` `hello`.
_17
// Provide the reference type `&String` using a type assertion
_17
//
_17
let helloRef = &hello as &String
_17
_17
helloRef.length // is `5`
_17
_17
// Create another reference to the `String` `hello`.
_17
// Provide the reference type `&String` using a type annotation instead
_17
//
_17
let alsoHelloRef: &String = &hello
_17
_17
// Invalid: Cannot create a reference without an explicit type
_17
//
_17
let unknownRef = &hello

The reference type must be a supertype of the referenced object's type.


_10
// Invalid: Cannot create a reference to `hello`
_10
// typed as `&Int`, as it has type `String`
_10
//
_10
let intRef = &hello as &Int

When creating a reference to an optional value, the result is an optional reference. If the referenced value is nil, the resulting reference itself will be nil. If the referenced value exists, then forcing the optional reference will yield a reference to that value:


_10
let nilValue: String? = nil
_10
let nilRef = &nilValue as &String? // r has type &String?
_10
let n = nilRef! // error, forced nil value
_10
_10
let strValue: String? = ""
_10
let strRef = &strValue as &String? // r has type &String?
_10
let n = strRef! // n has type &String

References are covariant in their base types. For example, &T is a subtype of &U, if T is a subtype of U.


_35
_35
// Declare a resource interface named `HasCount`,
_35
// that has a field `count`
_35
//
_35
resource interface HasCount {
_35
count: Int
_35
}
_35
_35
// Declare a resource named `Counter` that conforms to `HasCount`
_35
//
_35
resource Counter: HasCount {
_35
pub var count: Int
_35
_35
pub init(count: Int) {
_35
self.count = count
_35
}
_35
_35
pub fun increment() {
_35
self.count = self.count + 1
_35
}
_35
}
_35
_35
// Create a new instance of the resource type `Counter`
_35
// and create a reference to it, typed as `&Counter`,
_35
// so the reference allows access to all fields and functions
_35
// of the counter
_35
//
_35
let counter <- create Counter(count: 42)
_35
let counterRef: &Counter = &counter as &Counter
_35
_35
counterRef.count // is `42`
_35
_35
counterRef.increment()
_35
_35
counterRef.count // is `43`

References may be authorized or unauthorized.

Authorized references have the auth modifier, i.e. the full syntax is auth &T, whereas unauthorized references do not have a modifier.

Authorized references can be freely upcasted and downcasted, whereas unauthorized references can only be upcasted. Also, authorized references are subtypes of unauthorized references.


_39
_39
// Create an unauthorized reference to the counter,
_39
// typed with the restricted type `&{HasCount}`,
_39
// i.e. some resource that conforms to the `HasCount` interface
_39
//
_39
let countRef = &counter as &{HasCount}
_39
_39
countRef.count // is `43`
_39
_39
// Invalid: The function `increment` is not available
_39
// for the type `&{HasCount}`
_39
//
_39
countRef.increment()
_39
_39
// Invalid: Cannot conditionally downcast to reference type `&Counter`,
_39
// as the reference `countRef` is unauthorized.
_39
//
_39
// The counter value has type `Counter`, which is a subtype of `{HasCount}`,
_39
// but as the reference is unauthorized, the cast is not allowed.
_39
// It is not possible to "look under the covers"
_39
//
_39
let counterRef2: &Counter = countRef as? &Counter
_39
_39
// Create an authorized reference to the counter,
_39
// again with the restricted type `{HasCount}`, i.e. some resource
_39
// that conforms to the `HasCount` interface
_39
//
_39
let authCountRef = &counter as auth &{HasCount}
_39
_39
// Conditionally downcast to reference type `&Counter`.
_39
// This is valid, because the reference `authCountRef` is authorized
_39
//
_39
let counterRef3: &Counter = authCountRef as? &Counter
_39
_39
counterRef3.count // is `43`
_39
_39
counterRef3.increment()
_39
_39
counterRef3.count // is `44`

References are ephemeral, i.e. they cannot be stored. Instead, consider storing a capability and borrowing it when needed.

Reference validity

Ephemeral references stay valid throughout the course of the program. However, references to resources can become invalid during the execution of a program, if the referenced resource is moved or destroyed after taking the reference.


_11
let r <-create R()
_11
_11
// Take a reference to resource.
_11
let ref = &r as &R
_11
_11
// Then transfer the resource into an account.
_11
// This will invalidate all the references taken to the resource `r`.
_11
account.save(<-r, to: /storage/r)
_11
_11
// Static error, since the referenced resource has been moved.
_11
ref.id = 2

A reference is invalidated upon the first transfer of the underlying resource, regardless of the origin and the destination.


_10
let ref = &r as &R
_10
_10
// Moving a resource to a different variable invalidates all references to it.
_10
let r2 <- r
_10
_10
// Static error, since the referenced resource has been moved.
_10
ref.id = 2

info

Invalidations of storage references are not statically caught, but only at run-time.