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.
1let hello = "Hello"23// Create a reference to the `String` `hello`.4// Provide the reference type `&String` using a type assertion5//6let helloRef = &hello as &String78helloRef.length // is `5`910// Create another reference to the `String` `hello`.11// Provide the reference type `&String` using a type annotation instead12//13let alsoHelloRef: &String = &hello1415// Invalid: Cannot create a reference without an explicit type16//17let unknownRef = &hello
The reference type must be a supertype of the referenced object's type.
1// Invalid: Cannot create a reference to `hello`2// typed as `&Int`, as it has type `String`3//4let 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:
1let nilValue: String? = nil2let nilRef = &nilValue as &String? // r has type &String?3let n = nilRef! // error, forced nil value45let strValue: String? = ""6let strRef = &strValue as &String? // r has type &String?7let 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
.
1// Declare a resource interface named `HasCount`,2// that has a field `count`3//4resource interface HasCount {5count: Int6}78// Declare a resource named `Counter` that conforms to `HasCount`9//10resource Counter: HasCount {11pub var count: Int1213pub init(count: Int) {14self.count = count15}1617pub fun increment() {18self.count = self.count + 119}20}2122// Create a new instance of the resource type `Counter`23// and create a reference to it, typed as `&Counter`,24// so the reference allows access to all fields and functions25// of the counter26//27let counter <- create Counter(count: 42)28let counterRef: &Counter = &counter as &Counter2930counterRef.count // is `42`3132counterRef.increment()3334counterRef.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.
1// Create an unauthorized reference to the counter,2// typed with the restricted type `&{HasCount}`,3// i.e. some resource that conforms to the `HasCount` interface4//5let countRef = &counter as &{HasCount}67countRef.count // is `43`89// Invalid: The function `increment` is not available10// for the type `&{HasCount}`11//12countRef.increment()1314// Invalid: Cannot conditionally downcast to reference type `&Counter`,15// as the reference `countRef` is unauthorized.16//17// The counter value has type `Counter`, which is a subtype of `{HasCount}`,18// but as the reference is unauthorized, the cast is not allowed.19// It is not possible to "look under the covers"20//21let counterRef2: &Counter = countRef as? &Counter2223// Create an authorized reference to the counter,24// again with the restricted type `{HasCount}`, i.e. some resource25// that conforms to the `HasCount` interface26//27let authCountRef = &counter as auth &{HasCount}2829// Conditionally downcast to reference type `&Counter`.30// This is valid, because the reference `authCountRef` is authorized31//32let counterRef3: &Counter = authCountRef as? &Counter3334counterRef3.count // is `43`3536counterRef3.increment()3738counterRef3.count // is `44`
References are ephemeral, i.e. they cannot be stored. Instead, consider storing a capability and borrowing it when needed.
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.
1let r <-create R()23// Take a reference to resource.4let ref = &r as &R56// Then transfer the resource into an account.7// This will invalidate all the references taken to the resource `r`.8account.save(<-r, to: /storage/r)910// Static error, since the referenced resource has been moved.11ref.id = 2
A reference is invalidated upon the first transfer of the underlying resource, regardless of the origin and the destination.
1let ref = &r as &R23// Moving a resource to a different variable invalidates all references to it.4let r2 <- r56// Static error, since the referenced resource has been moved.7ref.id = 2
Invalidations of storage references are not statically caught, but only at run-time.