> ## Documentation Index
> Fetch the complete documentation index at: https://companyname-a7d5b98e-ton-storage.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Lazy loading

export const Aside = ({type = "note", title = "", icon = "", iconType = "regular", children}) => {
  const asideVariants = ["note", "tip", "caution", "danger"];
  const asideComponents = {
    note: {
      outerStyle: "border-sky-500/20 bg-sky-50/50 dark:border-sky-500/30 dark:bg-sky-500/10",
      innerStyle: "text-sky-900 dark:text-sky-200",
      calloutType: "note",
      icon: <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-sky-500" aria-label="Note">
          <path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.3C10.14 1.3 12.7 3.86 12.7 7C12.7 10.14 10.14 12.7 7 12.7C5.48908 12.6974 4.0408 12.096 2.97241 11.0276C1.90403 9.9592 1.30264 8.51092 1.3 7C1.3 3.86 3.86 1.3 7 1.3ZM7 0C3.14 0 0 3.14 0 7C0 10.86 3.14 14 7 14C10.86 14 14 10.86 14 7C14 3.14 10.86 0 7 0ZM8 3H6V8H8V3ZM8 9H6V11H8V9Z"></path>
        </svg>
    },
    tip: {
      outerStyle: "border-emerald-500/20 bg-emerald-50/50 dark:border-emerald-500/30 dark:bg-emerald-500/10",
      innerStyle: "text-emerald-900 dark:text-emerald-200",
      calloutType: "tip",
      icon: <svg width="11" height="14" viewBox="0 0 11 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="text-emerald-600 dark:text-emerald-400/80 w-3.5 h-auto" aria-label="Tip">
          <path d="M3.12794 12.4232C3.12794 12.5954 3.1776 12.7634 3.27244 12.907L3.74114 13.6095C3.88471 13.8248 4.21067 14 4.46964 14H6.15606C6.41415 14 6.74017 13.825 6.88373 13.6095L7.3508 12.9073C7.43114 12.7859 7.49705 12.569 7.49705 12.4232L7.50055 11.3513H3.12521L3.12794 12.4232ZM5.31288 0C2.52414 0.00875889 0.5 2.26889 0.5 4.78826C0.5 6.00188 0.949566 7.10829 1.69119 7.95492C2.14321 8.47011 2.84901 9.54727 3.11919 10.4557C3.12005 10.4625 3.12175 10.4698 3.12261 10.4771H7.50342C7.50427 10.4698 7.50598 10.463 7.50684 10.4557C7.77688 9.54727 8.48281 8.47011 8.93484 7.95492C9.67728 7.13181 10.1258 6.02703 10.1258 4.78826C10.1258 2.15486 7.9709 0.000106649 5.31288 0ZM7.94902 7.11267C7.52078 7.60079 6.99082 8.37878 6.6077 9.18794H4.02051C3.63739 8.37878 3.10743 7.60079 2.67947 7.11294C2.11997 6.47551 1.8126 5.63599 1.8126 4.78826C1.8126 3.09829 3.12794 1.31944 5.28827 1.3126C7.2435 1.3126 8.81315 2.88226 8.81315 4.78826C8.81315 5.63599 8.50688 6.47551 7.94902 7.11267ZM4.87534 2.18767C3.66939 2.18767 2.68767 3.16939 2.68767 4.37534C2.68767 4.61719 2.88336 4.81288 3.12521 4.81288C3.36705 4.81288 3.56274 4.61599 3.56274 4.37534C3.56274 3.6515 4.1515 3.06274 4.87534 3.06274C5.11719 3.06274 5.31288 2.86727 5.31288 2.62548C5.31288 2.38369 5.11599 2.18767 4.87534 2.18767Z"></path>
        </svg>
    },
    caution: {
      outerStyle: "border-amber-500/20 bg-amber-50/50 dark:border-amber-500/30 dark:bg-amber-500/10",
      innerStyle: "text-amber-900 dark:text-amber-200",
      calloutType: "warning",
      icon: <svg className="flex-none w-5 h-5 text-amber-400 dark:text-amber-300/80" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-label="Warning">
          <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
        </svg>
    },
    danger: {
      outerStyle: "border-red-500/20 bg-red-50/50 dark:border-red-500/30 dark:bg-red-500/10",
      innerStyle: "text-red-900 dark:text-red-200",
      calloutType: "danger",
      icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" className="text-red-600 dark:text-red-400/80 w-4 h-4" aria-label="Danger">
          <path d="M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zm41.6-48c-4.3 7.4-4.3 16.6 0 24l88.3 152.9c4.3 7.4 12.2 12 20.8 12l176.6 0c8.6 0 16.5-4.6 20.8-12L453.4 268c4.3-7.4 4.3-16.6 0-24L365.1 91.1c-4.3-7.4-12.2-12-20.8-12l-176.6 0c-8.6 0-16.5 4.6-20.8 12L58.6 244zM256 128c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"></path>
        </svg>
    }
  };
  let variant = type;
  let gotInvalidVariant = false;
  if (!asideVariants.includes(type)) {
    gotInvalidVariant = true;
    variant = "danger";
  }
  const iconVariants = ["regular", "solid", "light", "thin", "sharp-solid", "duotone", "brands"];
  if (!iconVariants.includes(iconType)) {
    iconType = "regular";
  }
  return <>
      <div className={`callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border ${asideComponents[variant].outerStyle}`} data-callout-type={asideComponents[variant].calloutType}>
        <div className="mt-0.5 w-4" data-component-part="callout-icon">
          {}
          {icon === "" ? asideComponents[variant].icon : <Icon icon={icon} iconType={iconType} size={14} />}
        </div>
        <div className={`text-sm prose min-w-0 w-full ${asideComponents[variant].innerStyle}`} data-component-part="callout-content">
          {gotInvalidVariant ? <p>
              <span className="font-bold">
                Invalid <code>type</code> passed!
              </span>
              <br />
              <span className="font-bold">Received: </span>
              {type}
              <br />
              <span className="font-bold">Expected one of: </span>
              {asideVariants.join(", ")}
            </p> : <>
              {title && <p className="font-bold">{title}</p>}
              {children}
            </>}
        </div>
      </div>
    </>;
};

Tolk language has a magic feature — the `lazy` keyword.
The compiler tracks exactly which fields are accessed, and automatically loads only those, skipping the rest.

In practice, prefer `lazy T.fromCell()` to a regular `T.fromCell()`.

<Aside type="tip">
  It is recommended to review [automatic serialization](/languages/tolk/features/auto-serialization) first.
</Aside>

## A short demo of `lazy`

Suppose there is a `Storage` struct in a wallet:

```tolk theme={null}
struct Storage {
    isSignatureAllowed: bool
    seqno: uint32
    subwalletId: uint32
    publicKey: uint256
    extensions: cell?
}

fun Storage.load() {
    return Storage.fromCell(contract.getData())
}
```

What does `Storage.load()` do? It unpacks a cell, populates all struct fields, checks consistency, and so on.

The magic of `lazy Storage.load()` is that it **does not load the entire cell**. Instead, unused fields are just skipped:

```tolk theme={null}
get fun publicKey() {
    val st = lazy Storage.load();
    // <-- here "skip 65 bits, preload uint256" is inserted
    return st.publicKey
}
```

The compiler tracks all control flow paths, inserts loading points as needed, groups unused fields to be skipped, etc. Best of all, this works with any type and any combination of fields.

## Even deeper than it seems

Take a look at the NFT collection:

```tolk theme={null}
struct NftCollectionStorage {
    adminAddress: address
    nextItemIndex: uint64
    content: Cell<CollectionContent>
    // ...
}

struct CollectionContent {
    metadata: cell
    minIndex: int32
    commonKey: uint256
}
```

Suppose a developer needs to read `content` and get `commonKey` from it:

```tolk theme={null}
val storage = lazy NftCollectionStorage.load();
// <-- here just "preload ref" is inserted
val contentCell = storage.content;
```

**First trick:** no need to **skip address and uint64**. To access a ref, it is not necessary to skip preceding data.

**Second trick:** having `content`, how to get `commonKey` from it? The answer: since `content` is a cell, load it… *lazily*:

```tolk theme={null}
val storage = lazy NftCollectionStorage.load();

// <-- "preload ref" inserted — to get `content`
// Cell<T>.load() unpacks a cell and returns T
val content = lazy storage.content.load();

// <-- "skip 32 bits, preload uint256" - to get commonKey
return content.commonKey;
```

A quick reminder: having `p: Cell<Point>`, it is not allowed to access `p.x` — the cell (reference) needs to be loaded first, either with `Point.fromCell(p)` or `p.load()`. Both can be used with `lazy`.

## Lazy matching

Similarly, a union type (an incoming message) can be read with `lazy`:

```tolk theme={null}
struct (0x12345678) CounterIncrement { /* ... */ }
struct (0x23456789) CounterReset     { /* ... */ }

type MyMessage = CounterIncrement | CounterReset

fun onInternalMessage(in: InMessage) {
    val msg = lazy MyMessage.fromSlice(in.body);
    match (msg) {
        CounterReset => {
            assert (something) throw 403;
            // <-- here "load msg.initial" is inserted
            storage.counter = msg.initial;
        }
        // ...
    }
}
```

With `lazy` applied to unions:

1. No union is allocated on the stack upfront; matching and loading are deferred until needed.
2. `match` operates naturally by inspecting the slice prefix (opcode).
3. Within each branch, the compiler inserts loading points and skips unused fields — just like it does for structs.

**Lazy matching is highly efficient**, outperforming `if (op == OP_RESET)`.
It aligns perfectly with the TVM execution model, eliminating unnecessary stack operations.

## Lazy matching and `else`

Since lazy `match` for a union is done by inspecting the prefix (opcode), unmatched cases fall through to the `else` branch.

```tolk theme={null}
val msg = lazy MyMessage.fromSlice(in.body);
match (msg) {
    CounterReset => { /* ... */ }
    // ... handle all variants of the union

    // else - when nothing matched;
    // even input less than 32 bits, no "underflow" thrown
    else => {
        // for example
        throw 0xFFFF
    }
}
```

Without an explicit `else`, unpacking throws `error 63` by default, which is controlled by the `throwIfOpcodeDoesNotMatch` option in `fromSlice`.
The `else` branch allows inserting any custom logic.

<Aside type="caution">
  `else` in `match` by type is only allowed with `lazy` because it matches on prefixes.
  Without `lazy`, it's just a regular union, `else` is not allowed.
</Aside>

## Partial updating

The magic doesn't stop at reading. The `lazy` keyword also works seamlessly when **writing data back**.

Example: load a storage, use its fields for assertions, modify one field, and save it back:

```tolk theme={null}
var storage = lazy Storage.load();

assert (storage.validUntil > blockchain.now()) throw 123;
assert (storage.seqno == msg.seqno) throw 456;
// ...

storage.seqno += 1;
contract.setData(storage.toCell());   // <-- magic
```

The compiler is smart: `toCell()` **does not save all fields of the storage** since only `seqno` was modified.
Instead, after loading `seqno`, an *immutable tail* was saved — and is reused when writing back:

```tolk theme={null}
var storage = lazy Storage.load();
// actually, what was done:
// - load isSignatureAllowed, seqno
// - save immutable tail
// - load validUntil, etc.

// ... use all fields for reading

storage.seqno += 1;
storage.toCell();
// actually, what was done:
// - store isSignatureAllowed, seqno
// - store immutable tail
```

The compiler can even group unmodified fields in the middle, load them as a slice, and preserve that slice on write-back.

## Q: How does `lazy` skip unused fields?

When several consecutive fields are unused, the compiler tries to group them.
It works perfectly for fixed-size types such as `intN` or `bitsN`:

```tolk theme={null}
struct Demo {
    isAllowed: bool     // always 1 bit
    queryId: uint64     // always 64 bits
    crc: bits32         // always 32 bits
    next: RemainingBitsAndRefs
}

fun demo() {
    val obj = lazy Demo.fromSlice(someSlice);
    // <-- skip 1+64+32 = 97 bits
    obj.next;
}
```

In Fift assembler, "skip 97 bits" is generated to

```fift theme={null}
97 LDU
NIP
```

But **variable-width fields**, like `coins`, cannot be grouped.
And cannot be skipped in a single instruction: TVM has no "skip coins" feature.
The only possible way is to load, but ignore the result.
Similarly, for `address`: despite it's always 267 bits, it should be validated even if unused —
otherwise, binary data could be decoded wrong.

For such types, `lazy` cannot do anything better than "**load and ignore**".
In practice, `intN` types are very common, so grouping has an evident effect.
A trick "access a ref without skipping any data" also works fine.

## Q: What are the disadvantages of `lazy`?

In terms of gas consumption, `lazy fromSlice` is equal to or cheaper than regular `fromSlice`.
In the worst case — when all fields are accessed — it loads everything one by one, just like the non-lazy version.

However, there is a difference **unrelated to gas consumption**:

* If a slice is too small or contains extra data, `fromSlice` will throw.

* The `lazy` keyword selectively *picks* only the requested fields and **handles partially invalid input gracefully**. For example, given:

```tolk theme={null}
struct Point {
    x: int8
    y: int8
}

fun demo(s: slice) {
    val p = lazy Point.fromSlice(s);
    return p.x;
}
```

Since only `p.x` is accessed, an input of `FF` (8 bits) is acceptable even though `y` is missing.
Similarly, `FFFF0000` (16 bits of extra data) is also fine, as `lazy` ignores any data that is not requested.

In most cases, this isn't an issue.
For incoming messages, typically all fields are used (otherwise, why include them in the struct?).
Extra data in the input is typically harmless. The message can still be deserialized correctly.

<Aside type="note">
  Perhaps someday, `lazy` will become the default.

  For now, it remains a distinct keyword highlighting the lazy-loading capability — a key feature of Tolk.
</Aside>
