Sui.

帖子

分享您的知识。

article banner.
harry phan.
Apr 13, 2025
文章

对象访问控制和接收机制

这是 “Sui Move 中的亲子对象” 系列的第 2 部分. 你可以在这里阅读第 1 部分

对象访问控制传输:: receive 机制

因此,您已将对象X放入父对象P(通过将 X 转移到 P 的 ID),如何将其取回或使用?🤔 这就是 Sui 的特殊接收机制的用武之地.

当一个对象被转移到父对象时,它不会自动弹出. 它坐落在那里,归P所有. 要在交易中使用移除该子对象,您必须接收该子对象. Sui 使用接收票证和 transfer:: receive 函数提供了一种结构化的方法来实现此目的.

##收货单

可以将 Receiving 视为类型为 T 的子对象的索赔凭证. 在事务中,您不是直接将子对象(您尚未拥有)传递给函数,而是传递一个 Receiving,本质上是对 “父级 X 拥有的 ID 为 Y 的对象” 的引用. 这个 Receiving 是一个特殊的 Move 结构,它包含待接收对象的 ID、版本和摘要. 它只有掉落能力,这意味着它可以短暂存在但不能永久存储换句话说,这是交易内的一次性使用票.

你如何获得收据?通常,通过执行传输. 在可编程交易区块 (PTB) 中,一个步骤可以将对象C转移到父级P,这会生成一个接收票证供下一步使用. 如果子对象已经位于先前交易的父对象中,则可以提供接收作为新事务的输入(Sui SDK/CLI 允许您按其 ID 和父对象指定子对象以生成票证).

**重要:**接收不是对象本身,它尚未赋予您所有权. 它只是表示 “具有此 ID 的 T 类型的对象归该父对象所有,我打算将其接受. ”要真正抓取对象,你必须调用 transfer:: receive:


module example::toy_box {
    use sui::transfer::{Self, Receiving};

    struct Toy has key { id: UID, name: vector<u8> }
    struct Box has key { id: UID }

    /// Remove a Toy child from a Box, returning the Toy to the caller.
    public entry fun take_toy(box: &mut Box, toy_ticket: Receiving<Toy>): Toy {
        // Use the parent's UID and the Receiving ticket to get the Toy
        let toy = transfer::receive(&mut box.id, toy_ticket);
        // Now `toy` is an owned value we can return (or transfer to someone).
        toy
    }
}

在 take_toy 中,toy_ticket: Rece iving 表示属于盒子的玩具. 通过调用 transfer:: receive(&mut box.id,toy_ticket),我们调用 Sui 的原生逻辑来实际从盒子里检索玩具对象. 这做了一些关键的事情:

-它在运行时验证toy_ticket确实引用了盒子当前拥有的对象(使用父级的UID). 如果某些内容不匹配(错误的父对象,或者对象实际上不在那里),它将中止. -然后,它将实际的玩具对象作为交易中的自有值返回,这意味着现在玩具由我们的函数控制.

请注意,我们必须传递 &mut box.id. Sui 强制我们有一个对父级 UID 的可变引用**来调用 receive.

这是一种巧妙的访问控制:只有能够生成父级 &mut UID 的模块才能允许接收. 通常,父类型的定义模块将暴露诸如 take_toy 之类的内部调用 receive 的函数. 如果父模块不公开任何给出 &mut UID 的函数(直接或间接),则任何外部代码都无法抢夺其子级. 这样,家长的模块就可以控制访问孩子的方式和时间.

在示例中,Box 有一个 id: UID 字段. 由于 take_toy 在同一个模块中,它可以借用 &mut box.id. 外部模块或交易可以调用 take_toy(box_obj,票证),但它们自己无法在 Box 上调用 transfer:: receive,因为 box.id 是私有的.

**这种模式确保只有授权码才能找回孩子. **✅

**嵌套对象呢?**如果 Toy 的确是 Box 中的一个田地(比如 Box {id,toy: Toy}),我们就不需要任何接收物了——只要你有 &mut Box 就可以拿到这个玩具. 但这也意味着将其移除或分开处理更难(就像盒子的一部分). 对于子对象,我们将存储空间分开:玩具是分开的,必须明确取出. 这种明确性是 Sui 需要接听票据并接听电话的原因,它使取回孩子成为一项授权操作.

**可变引用要求:**你可能想知道,为什么 &mut UID 而不只是 &UID?Mutable 确保在接收操作期间父节点处于*锁定状态,从而防止并发修改,并确保调用者实际上有权修改该父级(通常意味着他们是所有者). 这是 Sui 动态所有权检查的一部分——通过对父母的 UID 进行可变借用,Sui 保证在你带出孩子时不会有其他交易或并行行动干扰. 这有点像在取出内容之前锁上父母的门.

**交易区块中的用法示例:**假设一个 Box 对象归爱丽丝所有,而她有一个玩具物体她想放进盒子里,然后也许 我们稍后再把它拿出来. 以下是它可能看起来不错的样子:

-**将玩具装在盒子上:**Alice 在 PTB 中呼叫转接:: public_transfer(玩具,@);这会将玩具转移到盒子下面(我们在这里使用 public_transfer 是因为这是一个交易环境——稍后会详细介绍). 现在这个玩具是盒子里的孩子了. -**在相同或另一笔交易中,取回玩具:**Alice 调用示例:: toy_box:: take_toy (box_obj,),其中 Receiving 指的是玩具. 在幕后,take_toy 调用接收、验证关系并返回玩具. 现在,玩具将成为交易的输出,通常会回到爱丽丝的地址下(因为我们是从输入函数返回的).

关键要点:

-默认情况下,子对象不可访问;您需要接收票证和相应的功能才能将其取出. -父模块决定如何检索(通过提供带有 &mut UID 的函数). -transfer:: receive 用于 在父模块的定义模块内用于该模块或其朋友的对象. 如果孩子的类型是在其他地方定义的,则需要另一种方法(输入 public_receive...).

在继续之前,还有一个细节:如果物体类型 T 只有按键能力(没有存储),Sui 会将其视为受到更多限制. ���类对象不能通过其模块之外的泛型代码接收. 实际上,这意味着,如果 T 仅限密钥并成为子级,则需要在自己的模块或使用自定义规则的父模块中处理其检索. 如果 T 也有存储,我们可以通过 public_receive 获得更大的灵活性. 接下来让我们来探讨一下.

  • Sui
2
分享
评论
.
我们使用 cookie 确保您在我们的网站上获得最佳体验。
更多信息