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//_77resource 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//_77access(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`_77let counter: @Counter <- create Counter(count: 42)_77_77counter.count // is `42`_77_77counter.increment()_77_77counter.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//_77let 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//_77restrictedCounter.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//_77let unrestrictedCounter: @Counter <- restrictedCounter_77_77// Valid: The variable `unrestrictedCounter` has type `Counter`,_77// so all its functionality is available, including the function `increment`_77//_77unrestrictedCounter.increment()_77_77// Declare another resource type named `Strings`_77// which implements the resource interface `HasCount`_77//_77access(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//_77let 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
.
_41access(all) struct interface HasID {_41 access(all) let id: String_41}_41_41access(all) struct A: HasID {_41 access(all) let id: String_41_41 init(id: String) {_41 self.id = id_41 }_41}_41_41access(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_41let hasID1: {HasID} = A(id: "1")_41let 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//_41access(all) fun getID(_ value: {HasID}): String {_41 return value.id_41}_41_41let id1 = getID(hasID1)_41// `id1` is "1"_41_41let 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.