Skip to main content
Version: Next

Restricted Types

Structure and resource types can be restricted. Restrictions are interfaces. Restricted types only allow access to a subset of the members and functions of the type that is restricted, indicated by the restrictions.

The syntax of a restricted type is T{U1, U2, ... Un}, where T is the restricted type, a concrete resource or structure type, and the types U1 to Un are the restrictions, interfaces that T conforms to.

Only the members and functions of the union of the set of restrictions are available.

Restricted types are useful for increasing the safety in functions that are supposed to only work on a subset of the type. For example, by using a restricted type for a parameter's type, the function may only access the functionality of the restriction: If the function accidentally attempts to access other functionality, this is prevented by the static checker.


_77
// Declare a resource interface named `HasCount`,
_77
// which has a read-only `count` field
_77
//
_77
resource interface HasCount {
_77
access(all) let count: Int
_77
}
_77
_77
// Declare a resource named `Counter`, which has a writeable `count` field,
_77
// and conforms to the resource interface `HasCount`
_77
//
_77
access(all) resource Counter: HasCount {
_77
access(all) var count: Int
_77
_77
init(count: Int) {
_77
self.count = count
_77
}
_77
_77
access(all) fun increment() {
_77
self.count = self.count + 1
_77
}
_77
}
_77
_77
// Create an instance of the resource `Counter`
_77
let counter: @Counter <- create Counter(count: 42)
_77
_77
counter.count // is `42`
_77
_77
counter.increment()
_77
_77
counter.count // is `43`
_77
_77
// Move the resource in variable `counter` to a new variable `restrictedCounter`,
_77
// but typed with the restricted type `Counter{HasCount}`:
_77
// The variable may hold any `Counter`, but only the functionality
_77
// defined in the given restriction, the interface `HasCount`, may be accessed
_77
//
_77
let restrictedCounter: @Counter{HasCount} <- counter
_77
_77
// Invalid: Only functionality of restriction `Count` is available,
_77
// i.e. the read-only field `count`, but not the function `increment` of `Counter`
_77
//
_77
restrictedCounter.increment()
_77
_77
// Move the resource in variable `restrictedCounter` to a new variable `unrestrictedCounter`,
_77
// again typed as `Counter`, i.e. all functionality of the counter is available
_77
//
_77
let unrestrictedCounter: @Counter <- restrictedCounter
_77
_77
// Valid: The variable `unrestrictedCounter` has type `Counter`,
_77
// so all its functionality is available, including the function `increment`
_77
//
_77
unrestrictedCounter.increment()
_77
_77
// Declare another resource type named `Strings`
_77
// which implements the resource interface `HasCount`
_77
//
_77
access(all) resource Strings: HasCount {
_77
access(all) var count: Int
_77
access(self) var strings: [String]
_77
_77
init() {
_77
self.count = 0
_77
self.strings = []
_77
}
_77
_77
access(all) fun append(_ string: String) {
_77
self.strings.append(string)
_77
self.count = self.count + 1
_77
}
_77
}
_77
_77
// Invalid: The resource type `Strings` is not compatible
_77
// with the restricted type `Counter{HasCount}`.
_77
// Even though the resource `Strings` implements the resource interface `HasCount`,
_77
// it is not compatible with `Counter`
_77
//
_77
let counter2: @Counter{HasCount} <- create Strings()

In addition to restricting concrete types is also possible to restrict the built-in types AnyStruct, the supertype of all structures, and AnyResource, the supertype of all resources. For example, restricted type AnyResource{HasCount} is any resource type for which only the functionality of the HasCount resource interface can be used.

The restricted types AnyStruct and AnyResource can be omitted. For example, the type {HasCount} is any resource that implements the resource interface HasCount.


_41
access(all) struct interface HasID {
_41
access(all) let id: String
_41
}
_41
_41
access(all) struct A: HasID {
_41
access(all) let id: String
_41
_41
init(id: String) {
_41
self.id = id
_41
}
_41
}
_41
_41
access(all) struct B: HasID {
_41
access(all) let id: String
_41
_41
init(id: String) {
_41
self.id = id
_41
}
_41
}
_41
_41
// Create two instances, one of type `A`, and one of type `B`.
_41
// Both types conform to interface `HasID`, so the structs can be assigned
_41
// to variables with type `AnyResource{HasID}`: Some resource type which only allows
_41
// access to the functionality of resource interface `HasID`
_41
_41
let hasID1: {HasID} = A(id: "1")
_41
let hasID2: {HasID} = B(id: "2")
_41
_41
// Declare a function named `getID` which has one parameter with type `{HasID}`.
_41
// The type `{HasID}` is a short-hand for `AnyStruct{HasID}`:
_41
// Some structure which only allows access to the functionality of interface `HasID`.
_41
//
_41
access(all) fun getID(_ value: {HasID}): String {
_41
return value.id
_41
}
_41
_41
let id1 = getID(hasID1)
_41
// `id1` is "1"
_41
_41
let id2 = getID(hasID2)
_41
// `id2` is "2"

Only concrete types may be restricted, e.g., the restricted type may not be an array, the type [T]{U} is invalid.

Restricted types are also useful when giving access to resources and structures to potentially untrusted third-party programs through references, which are discussed in the next section.