Structs and Messages
Tact supports a number of primitive data types that are tailored for smart contract use. However, using individual means of storage often becomes cumbersome, so there are Structs and Messages which allow combining types together.
Structs
Structs can define complex data types that contain multiple fields of different types. They can also be nested.
Structs can also contain default fields and define fields of optional types. This can be useful if you have a lot of fields, but don’t want to keep having to specify common values for them in new instances.
Structs are also useful as return values from getters or other internal functions. They effectively allow a single getter to return multiple return values.
Note, that the last semicolon ;
in Struct declaration is optional and may be omitted:
The order of fields matters, as it corresponds to the resulting memory layout in TL-B schemas. However, unlike some languages with manual memory management, Tact does not have any padding between fields.
Messages
Messages can hold Structs in them:
Messages are almost the same thing as Structs with the only difference 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 since the contract can tell different types of messages apart based on this id.
Tact automatically generates those unique ids (opcodes) for every received Message, but this can be manually overwritten:
This is useful for cases where you want to handle certain opcodes of a given smart contract, such as Jetton standard. The short-list of opcodes this contract is able to process is given here in FunC. They serve as an interface to the smart contract.
Operations
Instantiate
Creation of Struct and Message instances resembles function calls, but instead of paretheses ()
one needs to specify arguments in braces {}
(curly brackets):
When the name of a variable or constant assigned to a field coincides with the name of such field, Tact provides a handy syntactic shortcut sometimes called field punning. With it, you don’t have to type more than it’s necessary:
Convert to a Cell
, .toCell()
It’s possible to convert an arbitrary Struct or Message to the Cell
type by using the .toCell()
extension function:
Obtain from a Cell
or Slice
, .fromCell()
and .fromSlice()
Instead of manually parsing a Cell
or Slice
via a series of relevant .loadSomething()
function calls, one can use .fromCell()
and .fromSlice()
extension functions for converting the provided Cell
or Slice
into the needed Struct or Message.
Those extension functions only attempt to parse a Cell
or Slice
according to the structure of your Struct or Message. In case layouts don’t match, various exceptions may be thrown — make sure to wrap your code in try...catch
blocks to prevent unexpected results.
Conversion laws
Whenever one converts between Cell
/Slice
and Struct/Message via .toCell()
and .fromCell()
functions, the following laws hold: