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.
1// Declare a resource interface named `HasCount`,2// which has a read-only `count` field3//4resource interface HasCount {5pub let count: Int6}78// Declare a resource named `Counter`, which has a writeable `count` field,9// and conforms to the resource interface `HasCount`10//11pub resource Counter: HasCount {12pub var count: Int1314init(count: Int) {15self.count = count16}1718pub fun increment() {19self.count = self.count + 120}21}2223// Create an instance of the resource `Counter`24let counter: @Counter <- create Counter(count: 42)2526counter.count // is `42`2728counter.increment()2930counter.count // is `43`3132// Move the resource in variable `counter` to a new variable `restrictedCounter`,33// but typed with the restricted type `Counter{HasCount}`:34// The variable may hold any `Counter`, but only the functionality35// defined in the given restriction, the interface `HasCount`, may be accessed36//37let restrictedCounter: @Counter{HasCount} <- counter3839// Invalid: Only functionality of restriction `Count` is available,40// i.e. the read-only field `count`, but not the function `increment` of `Counter`41//42restrictedCounter.increment()4344// Move the resource in variable `restrictedCounter` to a new variable `unrestrictedCounter`,45// again typed as `Counter`, i.e. all functionality of the counter is available46//47let unrestrictedCounter: @Counter <- restrictedCounter4849// Valid: The variable `unrestrictedCounter` has type `Counter`,50// so all its functionality is available, including the function `increment`51//52unrestrictedCounter.increment()5354// Declare another resource type named `Strings`55// which implements the resource interface `HasCount`56//57pub resource Strings: HasCount {58pub var count: Int59access(self) var strings: [String]6061init() {62self.count = 063self.strings = []64}6566pub fun append(_ string: String) {67self.strings.append(string)68self.count = self.count + 169}70}7172// Invalid: The resource type `Strings` is not compatible73// with the restricted type `Counter{HasCount}`.74// Even though the resource `Strings` implements the resource interface `HasCount`,75// it is not compatible with `Counter`76//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
.
1pub struct interface HasID {2pub let id: String3}45pub struct A: HasID {6pub let id: String78init(id: String) {9self.id = id10}11}1213pub struct B: HasID {14pub let id: String1516init(id: String) {17self.id = id18}19}2021// Create two instances, one of type `A`, and one of type `B`.22// Both types conform to interface `HasID`, so the structs can be assigned23// to variables with type `AnyResource{HasID}`: Some resource type which only allows24// access to the functionality of resource interface `HasID`2526let hasID1: {HasID} = A(id: "1")27let hasID2: {HasID} = B(id: "2")2829// Declare a function named `getID` which has one parameter with type `{HasID}`.30// The type `{HasID}` is a short-hand for `AnyStruct{HasID}`:31// Some structure which only allows access to the functionality of interface `HasID`.32//33pub fun getID(_ value: {HasID}): String {34return value.id35}3637let id1 = getID(hasID1)38// `id1` is "1"3940let 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.