Skip to content

Commit 0c9e3d8

Browse files
novusnotagithub-actions[bot]
authored andcommitted
feat(docs): destructuring statement (tact-lang#964)
1 parent 9307351 commit 0c9e3d8

File tree

2 files changed

+156
-1
lines changed

2 files changed

+156
-1
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Docs: Google Analytics tags per every page: PR [#921](https://github.com/tact-lang/tact/pull/921)
1616
- Docs: Added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958)
1717
- 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)
18-
- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#969](https://github.com/tact-lang/tact/pull/969)
18+
- 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), PR [#969](https://github.com/tact-lang/tact/pull/969)
1919
- Docs: automatic links to Web IDE from all code blocks: PR [#994](https://github.com/tact-lang/tact/pull/994)
2020
- The `SendDefaultMode` send mode constant to the standard library: PR [#1010](https://github.com/tact-lang/tact/pull/1010)
2121
- Docs: initial semi-automated Chinese translation of the documentation: PR [#942](https://github.com/tact-lang/tact/pull/942)

docs/src/content/docs/book/statements.mdx

+155
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ title: Statements
33
description: "This page lists all the statements in Tact, which can appear anywhere in the function bodies"
44
---
55

6+
import { Badge } from '@astrojs/starlight/components';
7+
68
The following statements can appear anywhere in the [function](/book/functions) body.
79

810
## `let` statement {#let}
@@ -96,6 +98,157 @@ value += 5; // augmented assignment (one of the many, see below)
9698

9799
:::
98100

101+
## Destructuring assignment
102+
103+
<Badge text="Available since Tact 1.6" variant="tip" size="medium"/><p/>
104+
105+
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.
106+
107+
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.
108+
109+
```tact {6}
110+
// Definition of Example
111+
struct Example { number: Int }
112+
113+
// An arbitrary helper function
114+
fun get42(): Example { return Example { number: 42 } }
115+
116+
fun basic() {
117+
// Basic syntax of destructuring assignment (to the left of "="):
118+
let Example { number } = get42();
119+
// ------- ------ -------
120+
// ↑ ↑ ↑
121+
// | | gives the Example Struct
122+
// | definition of "number" variable, derived
123+
// | from the field "number" in Example Struct
124+
// target structure type "Example"
125+
// to destructure fields from
126+
127+
// Same as above, but with an instantiation
128+
// to showcase how destructuring syntax mirrors it:
129+
let Example { number } = Example { number: 42 };
130+
// ----------------------
131+
// ↑
132+
// instantiation of Example Struct
133+
134+
// Above examples of syntax are roughly equivalent
135+
// to the following series of statements:
136+
let example = Example { number: 42 };
137+
let number = example.number;
138+
}
139+
```
140+
141+
Just like in [instantiation](/book/expressions#instantiation), the trailing comma is allowed.
142+
143+
```tact
144+
struct Example { number: Int }
145+
146+
fun trailblazing() {
147+
let Example {
148+
number, // trailing comma inside variable list
149+
} = Example {
150+
number: 42, // trailing comma inside field list
151+
};
152+
}
153+
```
154+
155+
:::note
156+
157+
[Augmented assignment operators](/book/operators#augmented-assignment) do not make sense for such assignments and will therefore be reported as parsing errors:
158+
159+
```tact
160+
struct Example { number: Int }
161+
fun get42(): Example { return Example { number: 42 } }
162+
163+
fun basic() {
164+
let Example { number } += get42();
165+
// ^ this will result in the parse error:
166+
// expected "="
167+
}
168+
```
169+
170+
:::
171+
172+
To create a binding under a different variable name, specify it after the semicolon `:{:tact}`.
173+
174+
```tact
175+
// Similar definition, but this time field is called "field", not "number"
176+
struct Example { field: Int }
177+
178+
fun naming(s: Example) {
179+
let Example { field: varFromField } = s;
180+
// ------------ ↑
181+
// ↑ |
182+
// | instance of Example Struct, received
183+
// | as a parameter of the function "naming"
184+
// definition of "varFromField" variable, derived
185+
// from the field "field" in Example Struct
186+
}
187+
```
188+
189+
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].
190+
191+
```tact
192+
// "first" goes first, then goes "second"
193+
struct Two { first: Int; second: String }
194+
195+
fun order(s: Two) {
196+
let Two { second, first } = s;
197+
// ------ -----
198+
// ↑ ↑
199+
// | this variable will be of type Int,
200+
// | same as the "first" field on Struct Two
201+
// this variable will be of type String,
202+
// same as the "second" field in Struct Two
203+
}
204+
```
205+
206+
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:
207+
208+
```tact
209+
// "first" goes first, then goes "second"
210+
struct Two { first: Int; second: String }
211+
212+
fun discard(s: Two) {
213+
let Two { second: _, first } = s;
214+
// ---
215+
// ↑
216+
// discards the "second" field, only taking the "first"
217+
}
218+
```
219+
220+
To completely ignore the rest of the fields, use `..` at the end of the list:
221+
222+
```tact
223+
struct Many { one: Int; two: Int; three: Int; fans: Int }
224+
225+
fun ignore(s: Many) {
226+
let Many { fans, .. } = s;
227+
// --
228+
// ↑
229+
// ignores all the unspecified fields,
230+
// defining only "fans"
231+
}
232+
```
233+
234+
:::caution
235+
236+
At the moment, destructuring of nested [Structs][s] or [Messages][m] isn't allowed. That is, the following won't work:
237+
238+
```tact
239+
struct First { nested: Second }
240+
struct Second { field: Int }
241+
242+
fun example() {
243+
let prep = First { nested: Second { field: 42 } };
244+
let First { nested: { field: thing } } = prep;
245+
// ^ this will result in the parse error:
246+
// expected "_", "A".."Z", or "a".."z"
247+
}
248+
```
249+
250+
:::
251+
99252
## Branches
100253

101254
Control the flow of the code.
@@ -415,3 +568,5 @@ foreach (_, _ in quartiles) {
415568
:::
416569

417570
[int]: /book/integers
571+
[s]: /book/structs-and-messages#structs
572+
[m]: /book/structs-and-messages#messages

0 commit comments

Comments
 (0)