- Go back to TOP(インデックスページへ)
- Declaring Attachments
- Creating Attachments
- Accessing Attachments
- Removing Attachments
Attachmentsは、開発者が構造体やリソースタイプ(宣言されていないものも含む)を、そのタイプのオリジナルの作成者が意図した動作を計画したり考慮したりすることなく、新しい機能で拡張できるように設計されたCadenceの機能です。
Declaring Attachments
Attachmentsは、attachmentキーワードで宣言され、これは新しい形式の複合宣言を使用して宣言されます。*attachment for : { ... }*ここで、Attachmentsの関数とフィールドは本体で宣言されます。したがって、以下はAttachmentsの合法(有効)な宣言の例となります。
access(all)
attachment Foo for MyStruct {
// ...
}
attachment Bar for MyResource: MyResourceInterface {
// ...
}
attachment Baz for MyInterface: MyOtherInterface {
// ...
}
他のすべての型宣言と同様に、Attachmentsはallのアクセス権限で宣言される必要があります。
Attachmentsの種類(構造体またはリソース)を指定する必要はありません。その種類は、拡張する型と同じ種類になるからです。ベース型(base type)は、具体的な複合型(composite type)またはインターフェースのいずれかであることに注意してください。前者の場合、Attachmentsは、そのベース型に明確に属する値でのみ使用可能であり、インターフェースの場合、Attachmentsは、そのインターフェースに準拠する任意のタイプで使用可能です。
Attachmentsの本体は、複合型と同じ宣言規則に従います。特に、フィールド・メンバーと関数メンバーの両方を持つことができ、フィールド・メンバーはすべて初期化子(init)で初期化する必要があります。リソース・メンバーを持つことができるのは、リソース・タイプであるAttachmentsのみです。
selfキーワードはアタッチメント本体で使用できますが、複合型とは異なり、selfは複合型ではなく参照型です。Aに対するアタッチメント宣言では、selfの型はAへの参照となり、他の複合型宣言のようにAではなくなります。この参照が持つ特定の権限は、self参照が現れるメンバ関数に関連付けられたアクセス修飾子に依存し、以下で詳しく説明します。
Attachmentのリソースがdestroyされた場合、そのAttachmentはすべて不特定の順序でdestroyされます。この場合のAttachmentの破棄順序について唯一保証されているのは、ベースリソース(base resource)が最後に破棄されるということです。
Attachmentの本文内にはbaseキーワードも利用可能であり、Attachmentのベース値への参照が含まれています。つまり、Attachmentが添付されている複合型です。したがって、その型はAttachmentの宣言されたベース型への参照となります。つまり、access(all) attachment Foo for Barと宣言されたAttachmentの場合、Fooのbaseフィールドの型は*&Bar*となります。したがって、例えば、以下のようなAttachmentの宣言は有効です。
access(all)
resource R {
access(all)
let x: Int
init (_ x: Int) {
self.x = x
}
access(all)
fun foo() { ... }
}
access(all)
attachment A for R {
access(all)
let derivedX: Int
init (_ scalar: Int) {
self.derivedX = base.x * scalar
}
access(all)
fun foo() {
base.foo()
}
}
外部による変異チェックやaccess controlの目的では、Attachmentはベースタイプとは別の宣言とみなされます。したがって、開発者はbase値の*access(self)フィールド(またはAttachmentが別のスマートコントラクトで定義されている場合はaccess(contract)*フィールド)にアクセスすることはできません。また、配列やディクショナリ型のフィールドを変更することもできません。
access(all)
resource interface SomeInterface {
access(all)
let b: Bool
access(self)
let i: Int
access(all)
let a: [String]
}
access(all)
attachment SomeAttachment for SomeContract.SomeStruct {
access(all)
let i: Int
init(i: Int) {
if base.b {
self.i = base.i // cannot access `i` on the `base` value
} else {
self.i = i
}
}
access(all)
fun foo() {
base.a.append("hello") // cannot mutate `a` outside of the composite where it was defined
}
}
Attachmentのメンバ関数内では、baseおよびself参照は、関数のアクセス修飾子が指定するものと同じ権限を有します。例えば、access(all) attachment A for Rとして宣言されたAttachmentにおいて、関数access(E) fun foo()の定義内では、baseの型はauth(E) &Rとなり、selfの型はauth(E) &Aとなります。したがって、以下の定義は有効です。
resource R {
access(E)
fun foo() {
//...
}
}
access(all)
attachment A for R {
access(E)
fun bar() {
base.foo() // available because `E` is required above, and thus `base` is type `auth(E) &R`.
}
}
しかし、これは動作しません(無効です)。
resource R {
access(E)
fun foo() {
//...
}
}
access(all)
attachment A for R {
access(self)
fun bar() {
base.foo() // unavailable because this function has `self` access, and thus `base` only is type `&R`.
}
}
ここで、selfおよびbase値に権限(entitlements)が伝播される方法の結果として、Attachment定義はベース値(base values)がサポートする権限のみをサポートできることに注意してください。すなわち、R用に定義されたAttachment Aは、その定義で権限Eを使用できるのは、Rがその定義(または準拠するインターフェースの定義)でEも使用している場合のみです。
Attachment Types
access(all) 宣言されたAttachment A for C { ... } の名目上の型は A となります。
アタッチメントは第一級の値(原文: first. class values)ではないため、その使用法は一定の制限を受けることに注意することが重要です。そのため、その使用方法には一定の制限があります。特に、それらの型は参照型の外側には現れません。例えば、Attachmentの宣言attachment A for X {}が与えられた場合、型A, A?, [A], fun(): Aは有効なtype annotationではありませんが、&A, &A?, [&A]とfun(): &Aは有効です。
Creating Attachments
Attachmentは、attach式を使用して作成され、この式では、Attachmentの初期化とベース値へのアタッチメントが単一の操作で実行されます。Attachmentは第一級の値ではなく、ベース値から独立して存在することはできず、単独で移動させることもできません。つまり、Attachmentコンストラクタを呼び出せるのは、attach式の中だけです。Attachment値の作成とAttachmentを密接に結合することで、ユーザーにとってアタッチメントの処理がよりシンプルになります。また、この理由により、リソース添え字は、attach式に現れる場合、明示的な<-移動演算子を必要としません。
attach式は、attachキーワード、添え字値のコンストラクタ呼び出し、toキーワード、および添え字の基本値を評価する式で構成されます。添え字の初期化子(init)に必要な引数はすべて、コンストラクタ呼び出しで指定されます。
access(all)
resource R {}
access(all)
attachment A for R {
init(x: Int) {
//...
}
}
// ...
let r <- create R()
let r2 <- attach A(x: 3) to <-r
toキーワードの右辺の式は、アタッチメントのベースのサブタイプである複合値として評価されなければならず、toの左辺のコンストラクタが呼び出される前に評価されます。つまり、base値はアタッチメントの初期化子(init)の中で利用可能ですが、作成中のアタッチメントはコンストラクタの実行が終了するまではbase上でアクセスできないことに注意することが重要です(以下の「Accessing Attachments」のセクションを参照)。
access(all)
resource interface I {}
access(all)
resource R: I {}
access(all)
attachment A for I {}
// ...
let r <- create R() // has type @R
let r2 <- attach A() to <-r // ok, because `R` is a subtype of `I`, still has type @R
アタッチメントはtype別にbaseに保存されるため、値に同時に存在できるアタッチメントは、各typeにつき1つだけです。 値にすでにアタッチメントが存在する際に、ユーザーがその値にさらにアタッチメントを追加しようとすると、Cadenceは実行時エラーを発生させます。 attach 式によって返される型は、to の右辺の式と同じ型です。アタッチメントを値にattach(アタッチ)しても、その型の変更は行われません。
Accessing Attachments
Attachmentは、type-indexを介して複合型でアクセスされます。複合型の値は、キーがtypeで値がAttachmentであるディクショナリのような機能を持ちます。複合型値vが与えられた場合、インデックス構文を使用して、v上のAという名前のAttachmentを検索することができます。
let a = v[A] // has type `&A?`
この構文では、A が名詞アタッチメント型であり、vがAの宣言された基本型のサブタイプである複合型を持つことが必要です。前述の通り、アタッチメントは第一級の値ではないため、このインデックス処理では、v上のアタッチメントへの参照が返され、アタッチメント自体は返されません。指定された型のアタッチメントが v上に存在しない場合、この式は nilを返します。
アタッチメントへのアクセスが許可される権限のセットは、ベース値が許可される権限のセットと同じです。例えば、Aの定義が以下のようになっている場合:
entitlement E
entitlement F
resource R {
access(E)
fun foo() {
// ...
}
access(F)
fun bar() {
// ...
}
}
attachment A for R {
access(E | F)
fun qux() {
// ...
}
}
// ...
let a = v[A]!
vが型*&Rを持つ場合、aの結果の型は未承認の &Aとなります。逆に、vが型 auth(E) &Rを持つ場合、aの型は同じものに承認されます。すなわち、auth(E) &Aとなります 最後に、vが参照(すなわち、R型の所有値)でない場合、aはAに対して「完全な権利」を持つことになります。Aによって言及されるすべての権利が与えられます。すなわち、この場合、aの型はauth(E, F) &A*となります。
これは、Identity権限マッピングの動作とほぼ同等です。実際、アタッチメントは、ベース値上のIdentityマッピング・フィールドであると考えることができます。
Removing Attachments
アタッチメントは、remove ステートメントを使用して値から削除することができます。このステートメントは、remove キーワード、削除するアタッチメントの名詞(原文: nominal)型、fromキーワード、およびアタッチメントを削除する対象となる値で構成されます。
from の右辺の値は、アタッチメントの型の宣言されたベースのサブタイプである複合値でなければなりません。
例えば、アタッチメントをサポートするタイプを持つリソース r からアタッチメント A を削除するには、以下のようになります。
remove A from r
ステートメントが実行された後、from の右辺にある複合値にはAttachmentは含まれなくなります。 値に remove キーワードの後に表示されるAttachmentが含まれない場合、このステートメントは効果を持ちません。
Attachmentは、どのタイプからでも任意の順序で削除することができます。そのため、ユーザーは、他のAttachmentの特定の動作に依存するようなアタッチメントを設計しないよう注意する必要があります。
Attachmentを含むリソースがdestroyされた場合、そのアタッチメントはすべて任意の順序でdestroyされます。
翻訳元->https://cadence-lang.org/docs/language/attachments