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 are created by using the &
operator, followed by the object,
the as
keyword, and the type through which they should be accessed.
The given type must be a supertype of the referenced object's type.
References have the type &T
, where T
is the type of the referenced object.
let hello = "Hello"
// Create a reference to the "Hello" string, typed as a `String`
//
let helloRef: &String = &hello as &String
helloRef.length // is `5`
// Invalid: Cannot create a reference to `hello`
// typed as `&Int`, as it has type `String`
//
let intRef: &Int = &hello as &Int
References are covariant in their base types.
For example, &T
is a subtype of &U
, if T
is a subtype of U
.
// Declare a resource interface named `HasCount`,
// that has a field `count`
//
resource interface HasCount {
count: Int
}
// Declare a resource named `Counter` that conforms to `HasCount`
//
resource Counter: HasCount {
pub var count: Int
pub init(count: Int) {
self.count = count
}
pub fun increment() {
self.count = self.count + 1
}
}
// Create a new instance of the resource type `Counter`
// and create a reference to it, typed as `&Counter`,
// so the reference allows access to all fields and functions
// of the counter
//
let counter <- create Counter(count: 42)
let counterRef: &Counter = &counter as &Counter
counterRef.count // is `42`
counterRef.increment()
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.
// Create an unauthorized reference to the counter,
// typed with the restricted type `&{HasCount}`,
// i.e. some resource that conforms to the `HasCount` interface
//
let countRef: &{HasCount} = &counter as &{HasCount}
countRef.count // is `43`
// Invalid: The function `increment` is not available
// for the type `&{HasCount}`
//
countRef.increment()
// Invalid: Cannot conditionally downcast to reference type `&Counter`,
// as the reference `countRef` is unauthorized.
//
// The counter value has type `Counter`, which is a subtype of `{HasCount}`,
// but as the reference is unauthorized, the cast is not allowed.
// It is not possible to "look under the covers"
//
let counterRef2: &Counter = countRef as? &Counter
// Create an authorized reference to the counter,
// again with the restricted type `{HasCount}`, i.e. some resource
// that conforms to the `HasCount` interface
//
let authCountRef: auth &{HasCount} = &counter as auth &{HasCount}
// Conditionally downcast to reference type `&Counter`.
// This is valid, because the reference `authCountRef` is authorized
//
let counterRef3: &Counter = authCountRef as? &Counter
counterRef3.count // is `43`
counterRef3.increment()
counterRef3.count // is `44`
References are ephemeral, i.e they cannot be stored. Instead, consider storing a capability and borrowing it when needed.