From 89933f0aff42b70ed2951230e7004121b8de3f16 Mon Sep 17 00:00:00 2001
From: Novus Nota <68142933+novusnota@users.noreply.github.com>
Date: Wed, 16 Oct 2024 05:23:41 +0200
Subject: [PATCH 1/5] feat(docs): destructuring statement
---
docs/src/content/docs/book/statements.mdx | 129 ++++++++++++++++++++++
1 file changed, 129 insertions(+)
diff --git a/docs/src/content/docs/book/statements.mdx b/docs/src/content/docs/book/statements.mdx
index 4db01b93c..5efe64e47 100644
--- a/docs/src/content/docs/book/statements.mdx
+++ b/docs/src/content/docs/book/statements.mdx
@@ -3,6 +3,8 @@ title: Statements
description: "This page lists all the statements in Tact, which can appear anywhere in the function bodies"
---
+import { Badge } from '@astrojs/starlight/components';
+
The following statements can appear anywhere in the [function](/book/functions) body.
## `let` statement {#let}
@@ -96,6 +98,131 @@ value += 5; // augmented assignment (one of the many, see below)
:::
+## Destructuring assignment
+
+
+
+The destructuring assignment is a concise way to unpack [Structs][s] and [Messages][m] into distinct variables. It mirrors the [instantiation syntax](/book/expressions#instantiation), but instead of creating a new [Struct][s] or [Message][m] it binds every field or some of the fields to their respective variables.
+
+The syntax is derived from the [`let` statement](#let), and instead of specifying the variable name directly it involves specifying the structure type on the left side of the [assignment operator `={:tact}`](/book/operators#assignment), which corresponds to the structure type of the value on the right side.
+
+```tact {6}
+// Definition of Example
+struct Example { number: Int }
+
+fun basic() {
+ // Basic syntax of destructuring assignment (to the left of "="):
+ let Example { number } = Example { number: 42 };
+ // ------- ------ ----------------------
+ // ↑ ↑ ↑
+ // | | instantiation of Example Struct
+ // | definition of "number" variable, derived
+ // | from the field "number" on Example Struct
+ // target structure type "Example"
+ // to destructure fields from
+
+ // The syntax above is equivalent to the following series of statements:
+ let example = Example { number: 42 };
+ let number = example.number;
+}
+```
+
+Just like in [instantiation](/book/expressions#instantiation), the trailing comma is allowed.
+
+```tact
+struct Example { number: Int }
+
+fun trailblazing() {
+ let Example {
+ number, // trailing comma inside variable list
+ } = Example {
+ number: 42, // trailing comma inside field list
+ };
+}
+```
+
+:::note
+
+ [Augmented assignment operators](/book/operators#augmented-assignment) do not make sense for such assignments and will therefore be reported as parsing errors:
+
+ ```tact
+ struct Example { number: Int }
+
+ fun basic() {
+ let Example { number } += Example { number: 42 };
+ // ^ this will result in the parse error:
+ // expected "="
+ }
+ ```
+
+:::
+
+To create a binding under a different variable name, specify it after the semicolon `:{:tact}`.
+
+```tact
+// Similar definition, but this time field is called "field", not "number"
+struct Example { field: Int }
+
+fun naming(s: Example) {
+ let Example { field: varFromField } = s;
+ // ------------ ↑
+ // ↑ |
+ // | instance of Example Struct, received
+ // | as a parameter of the function "naming"
+ // definition of "varFromField" variable, derived
+ // from the field "field" on Example Struct
+}
+```
+
+Note, that the order of bindings doesn't matter — all the fields retain their values and types under their names no matter the order in which they stand in their definition in the respective [Struct][s] or [Message][m].
+
+```tact
+// "first" goes first, then goes "second"
+struct Two { first: Int; second: String }
+
+fun order(s: Two) {
+ let Two { second, first } = s;
+ // ------ -----
+ // ↑ ↑
+ // | this variable will be of type Int,
+ // | same as the "first" field on Struct Two
+ // this variable will be of type String,
+ // same as the "second" field on Struct Two
+}
+```
+
+Destructuring assignment is exhaustive and requires specifying all the fields as variables. To deliberately ignore some of the fields, use an underscore `_{:tact}`, which would make fields value considered unused and discarded. Note, that such wildcard variable name `_{:tact}` cannot be accessed:
+
+```tact
+// "first" goes first, then goes "second"
+struct Two { first: Int; second: String }
+
+fun discard(s: Two) {
+ let Two { second: _, first } = s;
+ // ---
+ // ↑
+ // discards the "second" field, only taking the "first"
+}
+```
+
+:::caution
+
+ At the moment, destructuring of nested [Structs][s] or [Messages][m] isn't allowed. That is, the following won't work:
+
+ ```tact
+ struct First { nested: Second }
+ struct Second { field: Int }
+
+ fun example() {
+ let prep = First { nested: Second { field: 42 } };
+ let First { nested: { field: thing } } = prep;
+ // ^ this will result in the parse error:
+ // expected "_", "A".."Z", or "a".."z"
+ }
+ ```
+
+:::
+
## Branches
Control the flow of the code.
@@ -415,3 +542,5 @@ foreach (_, _ in quartiles) {
:::
[int]: /book/integers
+[s]: /book/structs-and-messages#structs
+[m]: /book/structs-and-messages#messages
From 3da7a0aac9232c41ac42ff5af9c019f53333ecda Mon Sep 17 00:00:00 2001
From: Novus Nota <68142933+novusnota@users.noreply.github.com>
Date: Wed, 16 Oct 2024 05:24:56 +0200
Subject: [PATCH 2/5] chore: CHANGELOG
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df5c4f98b..36419e715 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Docs: the `description` property to the frontmatter of the each page for better SEO: PR [#916](https://github.com/tact-lang/tact/pull/916)
- Docs: Google Analytics tags per every page: PR [#921](https://github.com/tact-lang/tact/pull/921)
- Ability to specify a compile-time method ID expression for getters: PR [#922](https://github.com/tact-lang/tact/pull/922) and PR [#932](https://github.com/tact-lang/tact/pull/932)
-- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856)
+- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#964](https://github.com/tact-lang/tact/pull/964)
### Changed
From 9889fb14e723316c0c4fd16d1cc047e07f103b14 Mon Sep 17 00:00:00 2001
From: Novus Nota <68142933+novusnota@users.noreply.github.com>
Date: Mon, 21 Oct 2024 17:51:00 +0200
Subject: [PATCH 3/5] chore: vary the examples
---
docs/src/content/docs/book/statements.mdx | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/docs/src/content/docs/book/statements.mdx b/docs/src/content/docs/book/statements.mdx
index 5efe64e47..87234f812 100644
--- a/docs/src/content/docs/book/statements.mdx
+++ b/docs/src/content/docs/book/statements.mdx
@@ -110,18 +110,29 @@ The syntax is derived from the [`let` statement](#let), and instead of specifyin
// Definition of Example
struct Example { number: Int }
+// An arbitrary helper function
+fun get42(): Example { return Example { number: 42 } }
+
fun basic() {
// Basic syntax of destructuring assignment (to the left of "="):
- let Example { number } = Example { number: 42 };
- // ------- ------ ----------------------
+ let Example { number } = get42();
+ // ------- ------ -------
// ↑ ↑ ↑
- // | | instantiation of Example Struct
+ // | | gives the Example Struct
// | definition of "number" variable, derived
// | from the field "number" on Example Struct
// target structure type "Example"
// to destructure fields from
- // The syntax above is equivalent to the following series of statements:
+ // Same as above, but with an instantiation
+ // to showcase how destructuring syntax mirrors it:
+ let Example { number } = Example { number: 42 };
+ // ----------------------
+ // ↑
+ // instantiation of Example Struct
+
+ // Above examples of syntax are roughly equivalent
+ // to the following series of statements:
let example = Example { number: 42 };
let number = example.number;
}
@@ -147,9 +158,10 @@ fun trailblazing() {
```tact
struct Example { number: Int }
+ fun get42(): Example { return Example { number: 42 } }
fun basic() {
- let Example { number } += Example { number: 42 };
+ let Example { number } += get42();
// ^ this will result in the parse error:
// expected "="
}
From ea429a1badd543d80dc8f1512233902bbe341c4d Mon Sep 17 00:00:00 2001
From: Novus Nota <68142933+novusnota@users.noreply.github.com>
Date: Thu, 24 Oct 2024 16:02:36 +0200
Subject: [PATCH 4/5] feat: add the `..` syntax
---
docs/src/content/docs/book/statements.mdx | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/docs/src/content/docs/book/statements.mdx b/docs/src/content/docs/book/statements.mdx
index 87234f812..45ce9d0eb 100644
--- a/docs/src/content/docs/book/statements.mdx
+++ b/docs/src/content/docs/book/statements.mdx
@@ -217,6 +217,20 @@ fun discard(s: Two) {
}
```
+To completely ignore the rest of the fields, use `..` at the end of the list:
+
+```tact
+struct Many { one: Int; two: Int; three: Int; fans: Int }
+
+fun ignore(s: Many) {
+ let Many { fans, .. } = s;
+ // --
+ // ↑
+ // ignores all the unspecified fields,
+ // defining only "fans"
+}
+```
+
:::caution
At the moment, destructuring of nested [Structs][s] or [Messages][m] isn't allowed. That is, the following won't work:
From b45048c4fcdb108da94b7d927af1725017878ade Mon Sep 17 00:00:00 2001
From: Novus Nota <68142933+novusnota@users.noreply.github.com>
Date: Mon, 28 Oct 2024 15:55:10 +0100
Subject: [PATCH 5/5] fix: suggestions from code review
---
docs/src/content/docs/book/statements.mdx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/src/content/docs/book/statements.mdx b/docs/src/content/docs/book/statements.mdx
index 45ce9d0eb..ce363f5a3 100644
--- a/docs/src/content/docs/book/statements.mdx
+++ b/docs/src/content/docs/book/statements.mdx
@@ -120,7 +120,7 @@ fun basic() {
// ↑ ↑ ↑
// | | gives the Example Struct
// | definition of "number" variable, derived
- // | from the field "number" on Example Struct
+ // | from the field "number" in Example Struct
// target structure type "Example"
// to destructure fields from
@@ -182,7 +182,7 @@ fun naming(s: Example) {
// | instance of Example Struct, received
// | as a parameter of the function "naming"
// definition of "varFromField" variable, derived
- // from the field "field" on Example Struct
+ // from the field "field" in Example Struct
}
```
@@ -199,11 +199,11 @@ fun order(s: Two) {
// | this variable will be of type Int,
// | same as the "first" field on Struct Two
// this variable will be of type String,
- // same as the "second" field on Struct Two
+ // same as the "second" field in Struct Two
}
```
-Destructuring assignment is exhaustive and requires specifying all the fields as variables. To deliberately ignore some of the fields, use an underscore `_{:tact}`, which would make fields value considered unused and discarded. Note, that such wildcard variable name `_{:tact}` cannot be accessed:
+Destructuring assignment is exhaustive and requires specifying all the fields as variables. To deliberately ignore some of the fields, use an underscore `_{:tact}`, which discards the considered field's value. Note, that such wildcard variable name `_{:tact}` cannot be accessed:
```tact
// "first" goes first, then goes "second"