友元模块
friend
语法用于声明当前模块信任的其他模块。
被信任的友元模块可以调用当前模块中所有具有 public(friend)
可见性的函数。
关于函数可见性的详细信息,请参阅 函数 章节中的_可见性_部分。
友元声明
模块可以通过友元声明语句来指定其他模块作为友元,格式包括:
-
friend <address::name>
— 使用完全限定模块名的友元声明,如下例所示:module 0x42::a { friend 0x42::b; }
-
friend <module-name-alias>
— 使用模块别名进行友元声明,其中模块别名通过use
语句引入:module 0x42::a { use 0x42::b; friend b; }
一个模块可以包含多个友元声明,所有友元模块的集合构成友元列表。
在下面的示例中,0x42::B
和 0x42::C
都被视为 0x42::A
的友元:
module 0x42::a {
friend 0x42::b;
friend 0x42::c;
}
与 use
语句不同,friend
只能在模块作用域中声明,而不能在表达式块作用域中声明。
友元声明可以出现在任何允许顶层构造(如 use
、function
、struct
等)的位置。
但为了可读性,建议将友元声明放在模块定义的开始部分。
注意友元的概念不适用于 Move 脚本:
- Move 脚本不能声明
friend
模块,因为这样做没有意义:脚本中定义的函数没有调用机制 - Move 模块也不能声明
friend
脚本,因为脚本是临时代码片段,永远不会发布到全局存储中
友元声明规则
友元声明需遵循以下规则:
-
模块不能将自己声明为友元:
module 0x42::m { friend Self; // 错误! // ^^^^ 不能将模块自身声明为友元 } module 0x43::m { friend 0x43::M; // 错误 // ^^^^^^^ 不能将模块自身声明为友元 }
-
友元模块必须能被编译器识别:
module 0x42::m { friend 0x42::nonexistent; // 错误! // ^^^^^^^^^^^^^^^^^ 未绑定模块 '0x42::nonexistent' }
-
友元模块必须位于同一账户地址下(注意:这不是技术限制,而是当前策略要求,未来_可能_会放宽):
module 0x42::m {} module 0x43::n { friend 0x42::m; // 错误! // ^^^^^^^ 不能将当前地址外的模块声明为友元 }
-
友元关系不能形成循环依赖:
不允许在友元关系中出现循环,例如
0x2::a
友元0x2::b
友元0x2::c
友元0x2::a
这样的关系链是不允许的。 更一般地说,声明友元模块时…(原文未完整)将当前模块添加为友元模块的依赖项(目的是让友元模块能够调用当前模块中的函数)。如果该友元模块已经被直接或间接使用,就会形成循环依赖。address 0x2 { module a { use 0x2::c; friend 0x2::b; public fun a() { c::c() } } module b { friend 0x2::c; // 错误! // ^^^^^^ 这个友元关系会创建循环依赖:'0x2::b' 是 '0x2::a' 的友元,后者使用了 '0x2::c',而 '0x2::c' 又是 '0x2::b' 的友元 } module c { public fun c() {} } }
-
模块的友元列表不能包含重复项。
address 0x42 { module a {} ```模块 m { 使用 0x42::a 作为 aliased_a; 友元 0x42::A; 友元 aliased_a; // 错误! // ^^^^^^^^^ 重复的友元声明 '0x42::a'。模块中的友元声明必须是唯一的 } }