---
title: Structs and Messages
description: "Structs can define complex data types that contain multiple fields of different types, while Messages also have a 32-bit header and are convenient for receiving and sending message bodies on TON Blockchain."
---

import { Badge } from '@astrojs/starlight/components';

Tact supports a number of [primitive data types](/book/types#primitive-types) that are tailored for smart contract use. However, using individual means of storage often becomes cumbersome, so there are [structs](#structs) and [Messages](#messages), which allow combining types together.

After successful compilation, Tact produces a [compilation report](/book/compile), which features all the declared structs and Messages, including those from the Core standard library. See the [Structures section of the compilation report](/book/compile#structures) for details.

:::caution

  Currently, circular types are **not** possible. This means that struct or message struct `A` can't have a field of struct or message struct type `B` if `B` has a field of type `A`.

  Therefore, the following code **won't** compile:

  ```tact
  struct A {
      circularFieldA: B;
  }

  struct B {
      impossibleFieldB: A;
  }
  ```

:::

## Structs

Structs can define complex data types that contain multiple fields of different types. They can also be nested.

```tact
struct Point {
    x: Int as int64;
    y: Int as int64;
}

struct Line {
    start: Point;
    end: Point;
}
```

Structs can also contain default fields and define fields of [optional types](/book/optionals). This can be useful if you have a lot of fields but don't want to keep specifying common values for them in [new instances](#instantiate).

```tact
struct Params {
    name: String = "Satoshi"; // default value

    age: Int?; // field with an optional type Int?
               // and default value of null

    point: Point; // nested structs
}
```

Structs are also useful as return values from getters or other internal functions. They effectively allow a single getter to return multiple values.

```tact
contract StructsShowcase {
    params: Params; // struct as a contract's persistent state variable

    init() {
        self.params = Params {
            point: Point {
                x: 4,
                y: 2,
            },
        };
    }

    get fun params(): Params {
        return self.params;
    }
}
```

Note that the last semicolon `;` in a struct definition is optional and may be omitted:

```tact
struct Mad { ness: Bool }

struct MoviesToWatch {
    wolverine: String;
    redFunnyGuy: String
}
```

The order of fields matters, as it corresponds to the resulting memory layout in [TL-B schemas](https://docs.ton.org/develop/data-formats/tl-b-language). However, unlike some languages with manual memory management, Tact does not have any padding between fields.

Consequently, structs cannot be empty and must declare at least one field.

## Messages

Messages can hold [structs](#structs) in them:

```tact
struct Point {
    x: Int;
    y: Int;
}

message Add {
    point: Point; // holds a struct Point
}
```

### Message opcodes

Messages are almost the same as [structs](#structs), with the only difference being that Messages have a 32-bit integer header in their serialization containing their unique numeric id, commonly referred to as an _opcode_ (operation code). This allows Messages to be used with [receivers](/book/receive), since the contract can distinguish different types of messages based on this id.

Tact automatically generates these unique ids (opcodes) for every received Message, which can be observed in the [Structures section of the compilation report](/book/compile#structures).

Additionally and unlike regular [structs](#structs), this allows Messages to be declared empty:

```tact
message TotallyValid {}
```

However, opcodes can be overridden manually:

```tact
// This Message overrides its unique id (opcode) with 411,
// which allows it to be recognized in the receiver functions.
message(411) InfoNotification {}

// This Message overrides its opcode with 0x7362d09c
message(0x7362d09c) TokenNotification {
    forwardPayload: Slice as remaining;
}

// Since those Messages have unique opcode prefixes,
// incoming message bodies can be differentiated based on them.
contract OpcodeRecognition {
    receive(msg: InfoNotification) {
        // ...
    }

    receive(msg: TokenNotification) {
        // ...
    }
}
```

This is useful in cases where you want to handle certain opcodes of a given smart contract, such as the [Jetton standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md). The messages and their respective opcodes that this contract can process are defined [here in the Tact's Jetton implementation](https://github.com/tact-lang/jetton/blob/3f02e1065b871cbab300e019f578c3fb0b19effa/src/contracts/base/messages.tact). They serve as an interface to the smart contract.

<Badge text="Available since Tact 1.6" variant="tip" size="small"/> A message opcode can be any [compile-time](/ref/core-comptime) expression that evaluates to a positive 32-bit integer, so the following is also valid:

```tact
// This Message overrides its unique id (opcode) with 898001897,
// which is the evaluated integer value of the specified compile-time expression
message((crc32("Tact") + 42) & 0xFFFF_FFFF) MsgWithExprOpcode {
    field: Int as uint4;
}
```

:::note

  For more in-depth information on this, see:\
  [Convert received messages to `op` operations](/book/func#convert-received-messages-to-op-operations)\
  [Internal message body layout in TON Docs](https://docs.ton.org/develop/smart-contracts/guidelines/internal-messages#internal-message-body)\
  [Messages of the Jetton implementation in Tact](https://github.com/tact-lang/jetton/blob/3f02e1065b871cbab300e019f578c3fb0b19effa/src/contracts/base/messages.tact)\
  [Common examples of working with Fungible Tokens (Jettons) in Tact](/cookbook/jettons)

:::

<Badge text="Available since Tact 1.6.7" variant="tip" size="small"/> A message opcode can be obtained by calling the `opcode()` method on any message type:

```tact
message(0x777) TripleAxe {
    prize: Int as uint32;
}

contract Example {
    receive(msg: TripleAxe) {
        dump(TripleAxe.opcode()); // 0x777
   }
}
```

## Operations

### Instantiate

The creation of [struct](#structs) and [Message](#messages) instances resembles [function calls](/book/expressions#static-function-call), but instead of parentheses `(){:tact}`, one needs to specify arguments in braces `{}{:tact}` (curly brackets):

```tact
struct StA {
    field1: Int;
    field2: Int;
}

message MsgB {
    field1: String;
    field2: String;
}

fun example() {
    // Instance of a struct StA
    StA {
        field1: 42,
        field2: 68 + 1, // trailing comma is allowed
    };

    // Instance of a Message MsgB
    MsgB {
        field1: "May the 4th",
        field2: "be with you!", // trailing comma is allowed
    };
}
```

When the name of a variable or constant assigned to a field coincides with the name of that field, Tact provides a handy syntactic shortcut sometimes called field punning. With it, you don't have to type more than necessary:

```tact
struct PopQuiz {
    vogonsCount: Int;
    nicestNumber: Int;
}

fun example() {
    // Let's introduce a couple of variables
    let vogonsCount: Int = 42;
    let nicestNumber: Int = 68 + 1;

    // You may instantiate the struct as usual and assign variables to fields,
    // but that can be a bit repetitive and tedious at times
    PopQuiz { vogonsCount: vogonsCount, nicestNumber: nicestNumber };

    // Let's use field punning and type less,
    // because our variable names happen to be the same as the field names
    PopQuiz {
        vogonsCount,
        nicestNumber, // trailing comma is allowed here too!
    };
}
```

:::note

  Because instantiation is an expression in Tact, it's also described on the related page: [Instantiation expression](/book/expressions#instantiation).

:::

### Convert to a `Cell`, `.toCell()` {#tocell}

It is possible to convert an arbitrary [struct](#structs) or [Message](#messages) to the [`Cell{:tact}`][cell] type by using the `.toCell(){:tact}` [extension function](/book/functions#extensions):

```tact
struct Big {
    f1: Int;
    f2: Int;
    f3: Int;
    f4: Int;
    f5: Int;
    f6: Int;
}

fun conversionFun() {
    dump(Big {
        f1: 10000000000, f2: 10000000000, f3: 10000000000,
        f4: 10000000000, f5: 10000000000, f6: 10000000000,
    }.toCell()); // x{...cell with references...}
}
```

:::note

  See these extension functions in the Reference:\
  [`Struct.toCell(){:tact}`](/ref/core-cells#structtocell)\
  [`Message.toCell(){:tact}`](/ref/core-cells#messagetocell)

:::

### Obtain from a `Cell` or `Slice`, `.fromCell()` and `.fromSlice()` {#fromcellslice}

Instead of manually parsing a [`Cell{:tact}`][cell] or [`Slice{:tact}`][slice] via a series of relevant `.loadSomething(){:tact}` function calls, one can use `.fromCell(){:tact}` and `.fromSlice(){:tact}` [extension functions](/book/functions#extensions) to convert the provided [`Cell{:tact}`][cell] or [`Slice{:tact}`][slice] into the needed [struct](#structs) or [Message](#messages).

These extension functions only attempt to parse a [`Cell{:tact}`][cell] or [`Slice{:tact}`][slice] according to the structure of your struct or Message. In case the layouts don't match, various exceptions may be thrown — make sure to wrap your code in [`try...catch{:tact}`](/book/statements#try-catch) blocks to prevent unexpected results.

```tact
struct Fizz { foo: Int }
message(100) Buzz { bar: Int }

fun constructThenParse() {
    let fizzCell = Fizz { foo: 42 }.toCell();
    let buzzCell = Buzz { bar: 27 }.toCell();

    let parsedFizz: Fizz = Fizz.fromCell(fizzCell);
    let parsedBuzz: Buzz = Buzz.fromCell(buzzCell);
}
```

:::note

  See these extension functions in the Reference:\
  [`Struct.fromCell(){:tact}`][st-fc]\
  [`Struct.fromSlice(){:tact}`][st-fs]\
  [`Message.fromCell(){:tact}`][msg-fc]\
  [`Message.fromSlice(){:tact}`][msg-fs]

:::

### Conversion laws

Whenever one converts between [`Cell{:tact}`][cell]/[`Slice{:tact}`][slice] and [struct](#structs)/[Message](#messages) via `.toCell(){:tact}` and `.fromCell(){:tact}` functions, the following laws hold:

* For any instance of type [struct](#structs)/[Message](#messages), calling `.toCell(){:tact}` on it and then applying `Struct.fromCell(){:tact}` (or `Message.fromCell(){:tact}`) to the result gives back a copy of the original instance:

```tact {8-9,13-14}
struct ArbitraryStruct { fieldNotFound: Int = 404 }
message(0x2A) ArbitraryMessage {}

fun lawOne() {
    let structInst = ArbitraryStruct {};
    let messageInst = ArbitraryMessage {};

    ArbitraryStruct.fromCell(structInst.toCell());   // = structInst
    ArbitraryMessage.fromCell(messageInst.toCell()); // = messageInst

    // Same goes for Slices, with .toCell().asSlice() and .fromSlice()

    ArbitraryStruct.fromSlice(structInst.toCell().asSlice());   // = structInst
    ArbitraryMessage.fromSlice(messageInst.toCell().asSlice()); // = messageInst
}
```

* For any [`Cell{:tact}`][cell] with the same [TL-B](https://docs.ton.org/develop/data-formats/tl-b-language) layout as a given [struct](#structs)/[Message](#messages), calling `Struct.fromCell(){:tact}` (or `Message.fromCell(){:tact}`) on it and then converting the result to a [`Cell{:tact}`][cell] via `.toCell(){:tact}` will give a copy of the original [`Cell{:tact}`][cell]:

```tact {9-10,15-16}
struct ArbitraryStruct { val: Int as uint32 }
message(0x2A) ArbitraryMessage {}

fun lawTwo() {
    // Using 32 bits to store 42 just so this cellInst can be
    // reused for working with both ArbitraryStruct and ArbitraryMessage
    let cellInst = beginCell().storeUint(42, 32).endCell();

    ArbitraryStruct.fromCell(cellInst).toCell();  // = cellInst
    ArbitraryMessage.fromCell(cellInst).toCell(); // = cellInst

    // Same goes for Slices, with .fromSlice() and .toCell().asSlice()
    let sliceInst = cellInst.asSlice();

    ArbitraryStruct.fromSlice(sliceInst).toCell().asSlice();  // = sliceInst
    ArbitraryMessage.fromSlice(sliceInst).toCell().asSlice(); // = sliceInst
}
```

[st-fc]: /ref/core-cells#structfromcell
[st-fs]: /ref/core-cells#structfromslice
[msg-fc]: /ref/core-cells#messagefromcell
[msg-fs]: /ref/core-cells#messagefromslice

[p]: /book/types#primitive-types
[cell]: /book/cells#cells
[slice]: /book/cells#slices
