---
title: Cells、Builders 和 Slices
description: Cells、Builders 和 Slices 是 TON 区块链的底层单元
---

[Cells](#cells)、[Builders](#builders) 和 [Slices](#slices) 是 TON 区块链的底层 [primitives][p]。 TON 区块链的虚拟机 [TVM][tvm] 使用cell来表示持久存储中的所有数据结构，以及内存中的大部分数据结构。

## Cells {#cells}

`Cell{:tact}`是一种 [primitive][p] 和数据结构，它[通常](#cells-kinds)由多达 $1023$ 个连续排列的比特和多达 $4$ 个指向其他 cell 的引用(refs)组成。 循环引用在 [TVM][tvm] 中是被禁止的，因此无法通过 TVM 的机制创建循环引用。这意味着，单元（cells）可以被视为自身的 [四叉树][quadtree] 或 [有向无环图（DAG）](https://en.wikipedia.org/wiki/Directed_acyclic_graph)。 智能合约代码本身由树形结构的cell表示。

单元（Cells）和[单元原语](#cells-immutability)是以位（bit）为导向的，而非字节（byte）为导向的：[TVM][tvm] 将存储在单元中的数据视为最多 $1023$ 位的序列（字符串或流），而不是字节。 如有必要，合约可以自由使用 $21$-bit 整数字段，并将其序列化为 [TVM][tvm] cell，从而使用更少的持久存储字节来表示相同的数据。

### 种类 {#cells-kinds}

虽然 [TVM][tvm] 类型 [`Cell{:tact}`](#cells) 指的是所有cell，但存在不同的cell类型，其内存布局也各不相同。 [前面](#cells) 描述的通常被称为 _普通_ (或简单) cell--这是最简单、最常用的cell，只能包含数据。 绝大多数关于cell及其用法的描述、指南和 [参考文献](/zh-cn/ref/core-cells) 都假定是普通cell。

其他类型的cell统称为 _exotic_ cell (或特殊cell)。 它们有时会出现在 TON 区块链上的区块和其他数据结构的实际表示中。 它们的内存布局和用途与普通cell大不相同。

所有cell的种类 (或子类型) 都由 $-1$ 和 $255$之间的整数编码。 普通cell用 $-1$编码，特殊cell可用该范围内的任何其他整数编码。 奇异cell的子类型存储在其数据的前 $8$ 位，这意味着有效的奇异cell总是至少有 $8$ 个数据位。

[TVM][tvm] 目前支持以下exotic cell子类型：

- [Pruned branch cell][c-pruned]，子类型编码为 $1$ - 它们代表删除的cell子树。
- [Library reference cell][c-library]，子类型编码为 $2$ - 它们用于存储库，通常在 [masterchain](/zh-cn/book/masterchain) 上下文中使用。
- [Merkle proof cell][c-mproof]，子类型编码为 $3$ - 它们用于验证其他cell的树数据的某些部分是否属于完整树。
- [Merkle update cell][c-mupdate]，子类型编码为 $4$ - 它们总是有两个引用，对这两个引用的行为类似于[默克尔证明][mproof]。

:::note[Useful links:]

  [TON Docs 中的Pruned branch cells][c-pruned]\
  [TON Docs 中的 Merkle 证明cell][c-mproof]\
  [TON Docs 中的 Merkle 更新cell][c-mupdate]\
  [TON Docs 中的简单证明验证示例][mproof]

:::

[c-pruned]: https://docs.ton.org/develop/data-formats/exotic-cells#pruned-branch
[c-library]: https://docs.ton.org/develop/data-formats/library-cells
[c-mproof]: https://docs.ton.org/develop/data-formats/exotic-cells#merkle-proof
[c-mupdate]: https://docs.ton.org/develop/data-formats/exotic-cells#merkle-update
[mproof]: https://docs.ton.org/develop/data-formats/exotic-cells#simple-proof-verifying-example

### Levels {#cells-levels}

作为 [四叉树][quadtree]，每个单元格都有一个名为 _level_ 的属性，它由 $0$ 和 $3$之间的整数表示。 [普通](#cells-kinds) cell的级别总是等于其所有引用级别的最大值。 也就是说，没有引用的普通 cell 的层级为 $0$。

[Exotic](#cells-kinds) cell有不同的规则来决定它们的层级，这些规则在[TON Docs 的本页](https://docs.ton.org/develop/data-formats/exotic-cells)上有描述。

### 序列化 {#cells-serialization}

在通过网络传输 cell 或在磁盘上存储 cell 之前，必须对其进行序列化。 有几种常用格式，如[标准 `Cell{:tact}` 表示法](#cells-representation)和 [BoC](#cells-boc)。

#### 标准表示法 {#cells-representation}

标准 [`Cell{:tact}`](#cells) 表示法是 [tvm.pdf](https://docs.ton.org/tvm.pdf) 中首次描述的 cells 通用序列化格式。 它的算法以八进制（字节）序列表示cell，首先将称为描述符的第一个 $2$ 字节序列化：

- _引用描述符_（Refs descriptor）根据以下公式计算：$r + 8 _ k + 32 _ l$，其中 $r$ 是 cell 中包含的引用数量（介于 $0$ 和 $4$ 之间），$k$ 是 cell 类型的标志（$0$ 表示[普通](#cells-kinds)，$1$ 表示[特殊](#cells-kinds)），$l$ 是
  cell 的[层级](#cells-levels)（介于 $0$ 和 $3$ 之间）。
- _位描述符_（Bits descriptor）根据以下公式计算：$\lfloor\frac{b}{8}\rfloor + \lceil\frac{b}{8}\rceil$，其中 $b$ 是 cell 中的位数（介于 $0$ 和 $1023$ 之间）。

然后，cell 本身的数据位被序列化为 $\lceil\frac{b}{8}\rceil$ $8$-bit octets（字节）。 如果 $b$ 不是 8 的倍数，则在数据位上附加一个二进制 $1$ 和最多六个二进制 $0$s。

接下来， $2$ 字节存储了引用的深度，即Cell树根（当前Cell）和最深引用（包括它）之间的cells数。 例如，如果一个cell只包含一个引用而没有其他引用，则其深度为 $1$，而被引用cell的深度为 $0$。

最后，为每个参考cell存储其标准表示的 [SHA-256][sha-2] 哈希值，每个参考cell占用 $32$ 字节，并递归重复上述算法。 请注意，不允许循环引用cell，因此递归总是以定义明确的方式结束。 请注意，不允许循环引用cell，因此递归总是以定义明确的方式结束。

如果我们要计算这个cell的标准表示的哈希值，就需要将上述步骤中的所有字节连接在一起，然后使用 [SHA-256][sha-2] 哈希值进行散列。 如果我们要计算这个cell的标准表示的哈希值，就需要将上述步骤中的所有字节连接在一起，然后使用 [SHA-256][sha-2] 哈希值进行散列。 这是[TVM][tvm]的[`HASHCU`和`HASHSU`指令](https://docs.ton.org/learn/tvm-instructions/instructions)以及 Tact 的[`Cell.hash(){:tact}`](/zh-cn/ref/core-cells#cellhash)和[`Slice.hash(){:tact}`](/zh-cn/ref/core-cells#slicehash)函数背后的算法。

#### Bag of Cells {#cells-boc}

如 [boc.tlb](https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/tl/boc.tlb#L25) [TL-B schema][tlb] 所述，Bag of Cells（简称 _BoC_）是一种将cell序列化和去序列化为字节数组的格式。

在 TON Docs 中阅读有关 BoC 的更多信息：[Bag of Cells](https://docs.ton.org/develop/data-formats/cell-boc#bag-of-cells)。

:::note

  关于[`cell{:tact}`](#cells)序列化的高级信息：[Canonical `Cell{:tact}` 序列化](https://docs.ton.org/develop/research-and-development/boc)。

:::

### 不变性 (Immutability) {#cells-immutability}

cell是只读和不可变的，但 [TVM][tvm] 中有两组主要的 [ordinary](#cells-kinds) cell操作指令：

- cell创建（或序列化）指令，用于根据先前保存的值和cell构建新cell；
- cell解析（或反序列化）指令，用于提取或加载之前通过序列化指令存储到cell中的数据。

此外，还有专门针对 [exotic](#cells-kinds) cell的指令来创建这些cell并期望它们的值。 此外，[exotic](#cells-kinds) cell 有专门的指令来创建它们并预期它们的值。不过，[普通(ordinary)](#cells-kinds) cell解析指令仍可用于 [exotic](#cells-kinds) cell，在这种情况下，它们会在反序列化尝试中被自动替换为 [普通(ordinary)](#cells-kinds) cell。

所有cell操作指令都需要将 [`Cell{:tact}`](#cells) 类型的值转换为 [`Builder{:tact}`](#builders)或 [`Slice{:tact}`](#slices)类型，然后才能修改或检查这些cell。

## Builders

`Builder{:tact}` 是一种用于使用cell创建指令的cell操作[基元][p]。 它们就像cell一样不可改变，可以用以前保存的值和cell构建新的cell。 与cells不同，`Builder{:tact}`类型的值只出现在[TVM][tvm]堆栈中，不能存储在持久存储中。 举例来说，这意味着类型为 `Builder{:tact}` 的持久存储字段实际上是以cell的形式存储的。

`Builder{:tact}` 类型表示部分组成的cell，为其定义了追加整数、其他cell、引用其他cell等快速操作：

- [核心库中的 `Builder.storeUint(){:tact}`][b-2]
- [核心库中的 `Builder.storeInt(){:tact}`][b-3]
- [核心库中的 `Builder.storeBool(){:tact}`][b-4]
- [核心库中的 `Builder.storeSlice(){:tact}`][b-5]
- [核心库中的 `Builder.storeCoins(){:tact}`][b-6]
- [核心库中的 `Builder.storeAddress(){:tact}`][b-7]
- [核心库中的 `Builder.storeRef(){:tact}`][b-8]

虽然您可以使用它们来[手动构建](#cnp-manually) cell，但强烈建议使用[结构体][structs]：[使用结构体构建cell](#cnp-structs)。

## Slices {#slices}

`Slice{:tact}` 是使用cell解析指令的cell操作[基元][p]。 与cell不同，它们是可变的，可以通过序列化指令提取或加载之前存储在cell中的数据。 此外，与cell不同，`Slice{:tact}` 类型的值只出现在 [TVM][tvm] 堆栈中，不能存储在持久存储区中。 举例来说，这就意味着类型为 `Slice{:tact}` 的持久存储字段实际上是以cell的形式存储的。

`Slice{:tact}` 类型表示部分解析cell的剩余部分，或位于此类cell内并通过解析指令从中提取的值（子cell）：

- [核心库中的`Slice.loadUint(){:tact}`][s-2]
- [核心库中的`Slice.loadInt(){:tact}`][s-3]
- [核心库中的`Slice.loadBool(){:tact}`][s-4]
- [核心库中的`Slice.loadBits(){:tact}`][s-5]
- [核心库中的`Slice.loadCoins(){:tact}`][s-6]
- [核心库中的`Slice.loadAddress(){:tact}`][s-7]
- [核心库中的`Slice.loadRef(){:tact}`][s-8]

虽然您可以将它们用于cell的 [手动解析](#cnp-manually)，但强烈建议使用 [结构体][structs]：[使用结构体解析cell](#cnp-structs)。

## 序列化类型

与 [`Int{:tact}`](/zh-cn/book/integers)类型的序列化选项类似，`Cell{:tact}`、`Builder{:tact}` 和`Slice{:tact}` 在以下情况下也有不同的值编码方式：

- 作为[合约](/zh-cn/book/contracts)和[特性](/zh-cn/book/types#traits)的[存储变量](/zh-cn/book/contracts#variables)，
- 以及 [Structs](/zh-cn/book/structs and-messages#structs) 和 [Messages](/zh-cn/book/structs and-messages#messages) 的字段。

```tact {2-3}
contract SerializationExample {
    someCell: Cell as remaining;
    someSlice: Slice as bytes32;

    // Constructor function,
    // necessary for this example contract to compile
    init() {
        self.someCell = emptyCell();
        self.someSlice = beginCell().storeUint(42, 256).asSlice();
    }
}
```

### `remaining` {#serialization-remaining}

`remaining{:tact}` 序列化选项可应用于 [`Cell{:tact}`](#cells)、[`Builder{:tact}`](#builders)和 [`Slice{:tact}`](#slices)类型的值。

它通过直接存储和加载cell值而不是作为引用来影响cell值的构建和解析过程。 它通过直接存储和加载cell值而不是作为引用来影响cell值的构建和解析过程。 与 [cell操作指令](#cells-immutability) 相似，指定 `remaining{:tact}` 就像使用 [`Builder.storeSlice(){:tact}`][b-5] 和 [`Slice.loadBits(){:tact}`][s-5] 而不是 [`Builder.storeRef(){:tact}`][b-8] 和 [`Slice.loadRef(){:tact}`][s-8]，后者是默认使用的。

此外，Tact 产生的 [TL-B][tlb] 表示也会发生变化：

```tact {3-5, 8-10}
contract SerializationExample {
    // By default
    cRef: Cell;    // ^cell in TL-B
    bRef: Builder; // ^builder in TL-B
    sRef: Slice;   // ^slice in TL-B

    // With `remaining`
    cRem: Cell as remaining;    // remainder<cell> in TL-B
    bRem: Builder as remaining; // remainder<builder> in TL-B
    sRem: Slice as remaining;   // remainder<slice> in TL-B

    // Constructor function,
    // necessary for this example contract to compile
    init() {
        self.cRef = emptyCell();
        self.bRef = beginCell();
        self.sRef = emptySlice();
        self.cRem = emptyCell();
        self.bRem = beginCell();
        self.sRem = emptySlice();
    }
}
```

其中，[TL-B][tlb] 语法中的 `^cell`、`^builder` 和 `^slice` 分别表示对 [`cell{:tact}`](#cells)、[`builder{:tact}`](#builders)和 [`slice{:tact}`](#slices)值的引用、而 `cell`、`builder` 或 `slice` 的 `remainder<…>` 则表示给定值将直接存储为 `Slice{:tact}`，而不是作为引用。

现在，举一个真实世界的例子，想象一下你需要注意到智能合约中的入站 [jetton][jetton] 传输并做出反应。 相应的 [信息][消息] 结构如下： 相应的 [信息][消息] 结构如下：

```tact /remaining/
message(0x7362d09c) JettonTransferNotification {
    queryId: Int as uint64;             // arbitrary request number to prevent replay attacks
    amount: Int as coins;               // amount of jettons transferred
    sender: Address;                    // address of the sender of the jettons
    forwardPayload: Slice as remaining; // optional custom payload
}
```

合同中的 [receiver][recv] 应该是这样的：

```tact
receive(msg: JettonTransferNotification) {
    // ... you do you ...
}
```

收到 [jetton][jetton] 传输通知消息后，其cell体会被转换为 [`Slice{:tact}`](#slices)，然后解析为 `JettonTransferNotification{:tact}` [消息][message]。在此过程结束时，`forwardPayload` 将包含原始信息cell的所有剩余数据。 在此过程结束时，`forwardPayload` 将包含原始信息cell的所有剩余数据。

在这里，将 `forwardPayload: Slice as remaining` 字段放在 `JettonTransferNotification{:tact}` [消息][message]中的任何其他位置都不会违反 [jetton][jetton] 标准。 这是因为 Tact 禁止在[Structs][结构]和[Messages][消息]的最后一个字段之外的任何字段中使用 `as remaining{:tact}`，以防止滥用合同存储空间并减少 gas 消耗。

:::note

  注意，通过 `as remaining{:tact}` 序列化的cell不能是 [可选](/zh-cn/book/optionals)。  也就是说，指定类似 `Cell? as remaining{:tact}`, `Builder? as remaining{:tact}` 或 `Slice? as remaining{:tact}` 会导致编译错误。

  另外请注意，将 `Cell{:tact}` 指定为[map](/zh-cn/book/maps) 值类型的 `remaining{:tact}` 会被视为错误，无法编译。

:::

### `bytes32` {#serialization-bytes32}

:::note

  由 [#94](https://github.com/tact-lang/tact-docs/issues/94) 解决。

:::

### `bytes64` {#serialization-bytes64}

:::note

  由 [#94](https://github.com/tact-lang/tact-docs/issues/94) 解决。

:::

## 操作

### 构建和解析 {#operations-cnp}

在 Tact 中，至少有两种构建和解析cell的方法：

- [手动](#cnp-manually)，其中涉及积极使用[`Builder{:tact}`](#builders)、[`Slice{:tact}`](#slices)和[相关方法](/zh-cn/ref/core-cells)。
- [使用结构体](#cnp-structs)，这是一种值得推荐且更加方便的方法。

#### 手动 {#cnp-manually}

| 通过 `Builder{:tact}`进行建造                | 通过 `slice{:tact}` 进行解析                |
| :------------------------------------- | :------------------------------------ |
| [`beginCell(){:tact}`][b-1]            | [`Cell.beginParse(){:tact}`][s-1]     |
| [`.storeUint(42, 7){:tact}`][b-2]      | [`Slice.loadUint(7){:tact}`][s-2]     |
| [`.storeInt(42, 7){:tact}`][b-3]       | [`Slice.loadInt(7){:tact}`][s-3]      |
| [`.storeBool(true){:tact}`][b-4]       | [`Slice.loadBool(true){:tact}`][s-4]  |
| [`.storeSlice(slice){:tact}`][b-5]     | [`Slice.loadBits(slice){:tact}`][s-5] |
| [`.storeCoins(42){:tact}`][b-6]        | [`Slice.loadCoins(42){:tact}`][s-6]   |
| [`.storeAddress(address){:tact}`][b-7] | [`Slice.loadAddress(){:tact}`][s-7]   |
| [`.storeRef(cell){:tact}`][b-8]        | [`Slice.loadRef(){:tact}`][s-8]       |
| [`.endCell(){:tact}`][b-9]             | [`Slice.endParse(){:tact}`][s-9]      |

[b-1]: /zh-cn/ref/core-cells#begincell
[b-2]: /zh-cn/ref/core-cells#builderstoreuint
[b-3]: /zh-cn/ref/core-cells#builderstoreint
[b-4]: /zh-cn/ref/core-cells#builderstorebool
[b-5]: /zh-cn/ref/core-cells#builderstoreslice
[b-6]: /zh-cn/ref/core-cells#builderstorecoins
[b-7]: /zh-cn/ref/core-cells#builderstoreaddress
[b-8]: /zh-cn/ref/core-cells#builderstoreref
[b-9]: /zh-cn/ref/core-cells#builderendcell
[s-1]: /zh-cn/ref/core-cells#cellbeginparse
[s-2]: /zh-cn/ref/core-cells#sliceloaduint
[s-3]: /zh-cn/ref/core-cells#sliceloadint
[s-4]: /zh-cn/ref/core-cells#sliceloadbool
[s-5]: /zh-cn/ref/core-cells#sliceloadbits
[s-6]: /zh-cn/ref/core-cells#sliceloadcoins
[s-7]: /zh-cn/ref/core-cells#sliceloadaddress
[s-8]: /zh-cn/ref/core-cells#sliceloadref
[s-9]: /zh-cn/ref/core-cells#sliceendparse

#### Using Structs {#cnp-structs}

[结构][struct]和[消息][messages]几乎就是活生生的[TL-B 模式][tlb]。 也就是说，它们本质上是用可维护、可验证和用户友好的 Tact 代码表达的[TL-B 模式][tlb]。 也就是说，它们本质上是用可维护、可验证和用户友好的 Tact 代码表达的[TL-B 模式][tlb]。

强烈建议使用它们及其 [方法](/zh-cn/book/functions#extension-function)，如 [`Struct.toCell(){:tact}`][st-tc]和 [`Struct.fromCell(){:tact}`][st-fc]，而不是手动构造和解析cell，因为这样可以得到更多声明性和不言自明的合约。

[上文](#cnp-manually)的手动解析示例可以使用[Structs][struct]重新编写，如果愿意，还可以使用字段的描述性名称：

```tact /fromCell/ /toCell/
// First Struct
struct Showcase {
    id: Int as uint8;
    someImportantNumber: Int as int8;
    isThatCool: Bool;
    payload: Slice;
    nanoToncoins: Int as coins;
    wackyTacky: Address;
    jojoRef: Adventure; // another Struct
}

// Here it is
struct Adventure {
    bizarre: Bool = true;
    time: Bool = false;
}

fun example() {
    // Basics
    let s = Showcase.fromCell(
        Showcase{
            id: 7,
            someImportantNumber: 42,
            isThatCool: true,
            payload: emptySlice(),
            nanoToncoins: 1330 + 7,
            wackyTacky: myAddress(),
            jojoRef: Adventure{ bizarre: true, time: false },
        }.toCell());
    s.isThatCool; // true
}
```

请注意，Tact 的自动布局算法是贪婪的。 请注意，Tact 的自动布局算法是贪婪的。例如，`struct Adventure{:tact}` 占用的空间很小，它不会以引用 [`Cell{:tact}`](#cells) 的形式存储，而是直接以 [`Slice{:tact}`](#slices) 的形式提供。

通过使用 [结构][struct] 和 [消息][messages]，而不是手动 [`Cell{:tact}`](#cells) 组成和解析，这些细节将被简化，在优化布局发生变化时也不会造成任何麻烦。

:::note[Useful links:]

  [Convert serialization](/zh-cn/book/func#convert-serialization)
  [核心库中的 `Struct.toCell(){:tact}` ][st-tc]\
  [核心库中的 `Struct.fromCell(){:tact}`][st-fc]\
  [核心库中的 `Struct.fromSlice(){:tact}`中][st-fs]\
  [核心库中的 `Message.toCell(){:tact}`][msg-tc]\
  [核心库中的 `Message.fromCell(){:tact}`][msg-fc]\
  [核心库中的 `Message.fromSlice(){:tact}`][msg-fs]

:::

[st-tc]: /zh-cn/ref/core-cells#structtocell
[st-fcc]: /zh-cn/ref/core-cells#structfromcell
[st-fs]: /zh-cn/ref/core-cells#structfromslice
[msg-tc]: /zh-cn/ref/core-cells#messagetocell
[msg-fc]: /zh-cn/ref/core-cells#messagefromcell
[msg-fs]: /zh-cn/ref/core-cells#messagefromslice

### 检查是否为空 {#operations-empty}

[`Cell{:tact}`](#cells)和[`Builder{:tact}`](#builders)都不能直接检查空性，需要先将它们转换为[`Slice{:tact}`](#slices)。

要检查是否有任何位，请使用[`Slice.dataEmpty(){:tact}`][s-de]。要检查是否存在引用，请使用[`Slice.refsEmpty(){:tact}`][s-re]。要同时检查这两项，请使用[`Slice.empty(){:tact}`][s-e]。 要检查是否存在引用，请使用 [`Slice.refsEmpty(){:tact}`][s-re]。 要同时检查这两个文件，请使用 [`Slice.empty(){:tact}`][s-e]。

如果[`Slice{:tact}`](#slices)不完全为空，也要抛出[exit code 9](/zh-cn/book/exit-codes#9)，请使用[`Slice.endParse(){:tact}`][s-ep]。

```tact
// Preparations
let someCell = beginCell().storeUint(42, 7).endCell();
let someBuilder = beginCell().storeRef(someCell);

// Obtaining our Slices
let slice1 = someCell.asSlice();
let slice2 = someBuilder.asSlice();

// .dataEmpty()
slice1.dataEmpty(); // false
slice2.dataEmpty(); // true

// .refsEmpty()
slice1.refsEmpty(); // true
slice2.refsEmpty(); // false

// .empty()
slice1.empty(); // false
slice2.empty(); // false

// .endParse()
try {
    slice1.endParse();
    slice2.endParse();
} catch (e) {
    e; // 9
}
```

:::note[Useful links:]

  [核心库中的 `Cell.asSlice(){:tact}`](/zh-cn/ref/core-cells#cellasslice)\
  [核心库中的 `Builder.asSlice(){:tact}`](/zh-cn/ref/core-cells#builderasslice)\
  [核心库中的 `Slice.dataEmpty(){:tact}`][s-de]\
  [核心库中的 `Slice.refsEmpty(){:tact}`][s-re]\
  [核心库中的 `Slice.empty(){:tact}`][s-e]\
  [核心库中的 `Slice.endParse(){:tact}`][s-ep]

:::

[s-de]: /zh-cn/ref/core-cells#slicedataempty
[s-re]: /zh-cn/ref/core-cells#slicerefsempty
[s-e]: /zh-cn/ref/core-cells#sliceempty
[s-ep]: /zh-cn/ref/core-cells#sliceendparse

### 检查是否相等 {#operations-equal}

不能使用二进制相等 [`=={:tact}`][bin-eq] 或不等式 [`!={:tact}`][bin-eq] 操作符直接比较 [`Builder{:tact}`](#builders) 类型的值。 但是，[`cell{:tact}`](#cells)和[`slice{:tact}`](#slices)类型的值可以。

直接比较：

```tact
let a = beginCell().storeUint(123, 8).endCell();
let aSlice = a.asSlice();

let b = beginCell().storeUint(123, 8).endCell();
let bSlice = b.asSlice();

let areCellsEqual = a == b; // true
let areCellsNotEqual = a != b; // false

let areSlicesEqual = aSlice == bSlice; // true
let areSlicesNotEqual = aSlice != bSlice; // false
```

请注意，通过 `=={:tact}` 或 `!={:tact}` 操作符进行的直接比较隐含地使用了[标准 `Cell{:tact}` 表示法](#cells-representation)的 [SHA-256](https://en.wikipedia.org/wiki/SHA-2#Hash_standard) 哈希值。

还可使用 `.hash(){:tact}` 进行显式比较：

```tact
let a = beginCell().storeUint(123, 8).endCell();
let aSlice = a.asSlice();

let b = beginCell().storeUint(123, 8).endCell();
let bSlice = b.asSlice();

let areCellsEqual = a.hash() == b.hash(); // true
let areCellsNotEqual = a.hash() != b.hash(); // false

let areSlicesEqual = aSlice.hash() == bSlice.hash(); // true
let areSlicesNotEqual = aSlice.hash() != bSlice.hash(); // false
```

:::note[Useful links:]

  [核心库中的 `Cell.hash(){:tact}`](/zh-cn/ref/core-cells#cellhash)/
  [核心库中的 `Slice.hash(){:tact}`](/zh-cn/ref/core-cells#slicehash)/
  [`=={:tact}`和`!={:tact}`][bin-eq]。

:::

[p]: /zh-cn/book/types#primitive-types
[struct]: /zh-cn/book/structs-and-messages#structs
[message]: /zh-cn/book/structs-and-messages#messages
[recv]: /zh-cn/book/contracts#receiver-functions
[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview
[tlb]: https://docs.ton.org/develop/data-formats/tl-b-language
[jetton]: https://docs.ton.org/develop/dapps/asset-processing/jettons
[sha-2]: https://en.wikipedia.org/wiki/SHA-2#Hash_standard
[四叉树]: https://en.wikipedia.org/wiki/Quadtree
[bin-eq]: /zh-cn/book/operators#binary-equality
