
Mojo 的 unavailable 装饰器让故意移除的 API在编译期精确报错【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojounavailable是 Mojo 中用于标记故意不可用 API的装饰器被标记的函数/方法在名称解析与重载决议阶段依然可见但任何引用都会在编译期被转换为一条作者手写的、带指引的错误信息可选附带 fix-it 自动改名建议。它是deprecated仅告警的硬阻断孪生兄弟目前已在标准库中用于拦截String的歧义位置索引、len(String)以及Pointer的空值布尔测试等场景。读完本文你将掌握unavailable的全部语法、与deprecated/stable的互斥规则、函数体书写约束以及它在 Mojo 编译管线中的底层实现原理含源码证据。本设计提案状态为Accepted (implemented)作者 Nathan Ward2026 年 6 月见 unavailable-decorator.md。一、它解决什么问题删除与弃用之间的空白地带当 API 被移除、改名或根本不该存在时API 作者今天只有两个都不令人满意的选择直接删除。用户会得到一条通用诊断use of unknown declaration parse或no matching function in call完全没有说明为什么它消失了、该用什么替代。打上deprecated。API 继续可用只产生一条警告——当这个 API必须不能被使用时警告就是错误的工具。unavailable恰好填补了这个空白。符号对名称解析和重载决议保持可见但任何使用都是硬错误错误文本由作者亲自撰写并可附带一个把调用改名为替代符号的 fix-itunavailable(parse was removed in 25.7; use parse_v2 instead) def parse(s: String) - Int: ... def caller(): _ parse(123) # error: parse was removed in 25.7; use parse_v2 instead为什么错误必须作者手写通用诊断的致命弱点是不知道上下文。编译器知道parse这个名字不见了但它不知道它是被改名了、被移除了、还是有安全风险而被禁用。而unavailable的消息写在声明处由最了解这个 API 历史的人作者提供因此能精确告诉用户改成什么、为什么。这也是它与直接删除方案的核心差异——删除会让名字解析失败而unavailable让名字解析成功但使用即报错从而能精准拦截单个重载而不影响兄弟重载见下文String的例子。二、驱动用例String 的 UTF-8 位置索引Mojo 的String是 UTF-8 编码的因此s[i]这种位置索引是有歧义的——它可以指第 i 个字节、第 i 个 Unicode 码点、或第 i 个用户可见字符字素簇。静默地选一个会出问题而直接删除__getitem__又会让看起来非常自然的s[i]产生一条晦涩的 no matching overload 错误。String的解决方案是把位置型__getitem__重载声明为unavailable见 string.mojo 中的真实实现struct String: doc_hidden unavailable( String does not support direct positional indexing like s[i] because Mojo strings are UTF-8 encoded, and the same position can mean three different things. Use one of: s[bytei] for a raw UTF-8 byte, s[codepointi] for a Unicode code point, or s[graphemei] for a user-visible character (grapheme cluster). ) def __getitem__( self, _index: Some[Indexer], / ) - StringSlice[origin_of(self)]: ...现在s[i]会解析到这个重载用户得到一条精确、有教育意义的错误指向s[bytei]/s[codepointi]/s[graphemei]而关键字形式的兄弟重载完全不受影响。标准库中的更多真实用例提案落地后标准库中已经实际使用unavailable拦截了多个看上去自然、实际错误的调用位置拦截目标指引的替代方案string.mojoString位置切片s[a:b]s[bytea:b]/s[codepointa:b]string.mojoString.__len__s.byte_length()/len(s.codepoints())/len(s.graphemes())string_span.mojoStringSpan.__len__同上字节数 / 码点数 / 字素簇数len.mojolen(StringSlice)顶层函数重载s.byte_length()/len(s.codepoints())/len(s.graphemes())pointer.mojoPointer.__bool__指针非空设计下Bool(ptr)无意义Optional[Pointer[...]]配合Bool(opt_ptr)/! None例如len的重载拦截len.mojodoc_hidden unavailable( len(String/StringSlice) is not supported because Mojo strings are UTF-8 encoded, so a single length is ambiguous: ... Use s.byte_length() or len(s.bytes()) for the number of UTF-8 bytes, len(s.codepoints()) for Unicode code points, or len(s.graphemes()) for grapheme clusters. ) def len(value: StringSlice) - Int: ...注意这些unavailable重载与真正的关键字重载__getitem__(*, byte: Int)等见 string.mojo并存于同一结构体中充分展示了按重载精准阻断的能力。三、完整语法与使用规则unavailable与deprecated完全镜像接受理由消息或替代符号两种形式# 位置参数形式必须是字符串字面量。 unavailable(dont use this; it never worked) def old_api(): ... # reason 关键字形式含义完全相同。 unavailable(reasondont use this; it never worked) def old_api2(): ... # use 命名一个替代符号。 unavailable(usenew_api) def renamed_api(): ... def new_api(): pass函数体必须为...unavailable函数的函数体必须是...即使函数带有非None返回类型——不需要return因为函数体永远不会被执行调用即编译错误struct StringLike: unavailable( no length for StringLike; use byte_length() or codepoint_length() ) def __len__(self) - Int: ... # ... 是必须的这里写 return 0 是错误。这一约束有 LIT 测试逐条验证见 unavailable_body.mojo无返回类型 / 有返回类型的函数、有返回类型的方法...函数体均被接受非...函数体被拒绝报错unexpected function body in unavailable function declaration, use ...。与deprecated、stable互斥unavailable与deprecated、stable互相排斥顺序无关。测试 unavailable_errors.mojo 覆盖了四种组合unavailable(dont use this) # expected-error below {{unavailable and deprecated cannot be used together}} deprecated(use something else) def unavailable_and_deprecated(): ... # 顺序颠倒同样报错 deprecated(use something else) # expected-error below {{unavailable and deprecated cannot be used together}} unavailable(dont use this) def deprecated_and_unavailable(): ...unavailable与stable同理报unavailable and stable cannot be used together。参数校验规则由测试直接印证unavailable_errors.mojo 完整定义了参数的合法性边界写法结果unavailable无参数错误unavailable requires a reason messageunavailable(NOT_A_STRING)非字符串错误reason argument must be a string literalunavailable(reason...)合法与位置参数等价unavailable(reasonNOT_A_STRING)错误同上unavailable(msg1, msg2)错误unavailable accepts either a reason message or a replacement symbol (with use)unavailable(messagex)错误unavailable must specify either a message or a symbol (with the use argument)unavailable(usenot_a_symbol)错误use must reference a symbol适用范围仅函数与方法unavailable目前只支持函数和方法。对 struct、trait、comptime alias 使用会报错unavailable can only be applied to functions and methods见 unavailable_errors.mojo。类型/别名级别的不可用被有意推迟详见备选方案一节。四、使用即报错不只拦截调用从 unavailable_errors.mojo 的测试可以看到错误在所有引用形式下触发而不只是直接调用直接调用unavailable_fn()→ 报错取函数引用var f unavailable_fn→ 同样报错测试注释明确指出 Taking a reference to an unavailable function also emits the error方法调用obj.unavailable_method()→ 报错取方法引用_ StructWithUnavailableMembers.unavailable_method→ 同样报错普通函数/方法不受影响normal_fn()、obj.normal_method()、var g normal_fn均无错误。跨模块场景同样生效从 imported_module.mojo 导入被标记的函数再调用会报出模块内写的错误消息use of unavailable function in another module。一个有意思的边界trait 需求不能用 unavailable 方法满足unavailable_errors.mojo 验证了结构体不能用unavailable方法去满足 trait 需求。错误发生在声明一致性conformance本身unavailable trait method foo从而阻止该类型被用于受该 trait 约束的泛型——因为不先声明被拒绝的conformance就永远到不了任何使用点trait Fooable: def foo(self): ... # expected-error below {{unavailable trait method foo}} struct UnavailableTraitImpl(Fooable, Movable where False): def __init__(out self): pass unavailable(unavailable trait method foo) def foo(self): ...泛型内部t.foo()绑定的是 trait 需求可用因此泛型函数本身不报错错误只在 conformance 检查处爆发一次从根上切断了该类型的 trait 使用路径。五、底层实现从装饰器到#lit.unavailable属性unavailable的落地涉及 Mojo 编译器前端的多个环节以下均有源码为证。1. 语法与语义检查MojoParser装饰器的解析与合法性校验在 MojoParser 中完成。核心文件是 StabilityMarkers.cpp 与配套头文件 StabilityMarkers.h。校验流程包括仅允许应用于函数/方法声明struct/trait/alias 直接报错参数形式检查reason 字符串 /use符号、互斥关键字等与deprecated、stable的互斥性检查。2. IR 表示#lit.unavailable属性LITDialect编译后的不可用信息以 MLIR 属性形式挂在函数声明上定义在 LITAttrs.tddef LIT_UnavailableInfoAttr : LITAttrUnavailableInfo, unavailable { let summary Unavailability information for a declaration; ... let parameters (ins StringAttr:$reason, OptionalParameterStringAttr:$replacement ); let assemblyFormat $reason (, $replacement^)? ; }它存储诊断消息reason和可选的替代标识符replacement来自unavailable(useX)。文档字符串明确写道Unlikedeprecated, references to declarations markedunavailableproduce an error rather than a warning.不同于deprecated对标记为unavailable的声明的引用产生错误而非警告。IR 生成的形状可直接从 LIT 测试的 CHECK 指令中看到unavailable.mojo# CHECK-LABEL: lit.fn unavailable_func # CHECK-SAME: unavailableInfo #lit.unavailablefunc ... # CHECK-SAME: unavailableInfo #lit.unavailableunavailable_func_use is unavailable, use unavailable_func_target instead, unavailable_func_target注意use形式会自动生成默认理由文本X is unavailable, use Y instead并同时带上替换符号。3. 错误发射checkUnavailableAndError使用点的错误发射在 StabilityMarkers.cpp 的checkUnavailableAndError中实现其逻辑为通过StabilityDecoratorInterface检查声明是否带unavailableisUnavailable()不带则直接返回取出reason字符串在使用点useLoc发出硬错误shared.emitError如果存在replacement且调用语法是直接调用或方法调用kDirectCall/kMethodCall附加一个FixIt::replaceToken的纯语法改名建议old()→new()附加一条声明位置备注X declared here帮助用户定位到被标记的声明。一个值得注意的实现细节源码注释中明确说明fix-it 只对直接调用/方法调用附加对运算符/下标语法不附加——因为把魔法方法名如__getitem__替换成别的符号在语法上不成立。同时use设计上只适用于调用签名完全一致的直接替代品纯 token 改名即可编译通过参数不同的替代方案应该使用reason消息来引导避免给出编译不过的修复建议。这一点与deprecated完全镜像同文件第 160-174 行的checkDeprecationAndWarn可见相同逻辑。4. 与 deprecated 的分工checkDeclUsageWarningsStabilityMarkers.cpp 的checkDeclUsageWarnings展示了三种稳定性装饰器的执行顺序unavailable 错误在任何使用点都会触发且永不被抑制注释Unavailability errors fire at every use site and are never suppressed.随后才执行 deprecation 警告和 stability 警告。这就是硬阻断语义的源码级保证——不像deprecated可以通过 allowlist 等方式抑制unavailable没有抑制通道。六、被否决与延期的备选方案提案记录的决策过程有助于理解当前设计边界复用deprecated加error级严重度标志否决。warn 与 error 的区别足够根本值得拥有独立的装饰器名意图在声明处一目了然独立的装饰器也让与deprecated/stable的互斥规则容易陈述和执行。直接删除 API否决。可发现性理由见动机一节删除产生无指引的通用诊断且无法在保留兄弟重载的情况下只精准拦截单个重载。允许unavailable用在类型和别名上延期。当前动机场景全部是函数和方法——尤其是重载级阻断对类型没有意义。限制初始范围让特性保持小巧若出现具体需求类型/别名级的不可用可以后续跟进。七、实践建议小结什么时候用unavailableAPI 被移除、改名、或从未该存在且必须阻止任何使用尤其是只想阻断某个重载而保留其他重载String的__getitem__是教科书级示范。什么时候用deprecatedAPI 还能用只是不推荐需要给用户一个迁移窗口。use与reason的选择签名完全一致的直接替代品用use获得自动 fix-it签名不同的替代品用reason写清楚迁移路径。函数体永远写...即使有返回类型。可用性边界仅函数与方法struct/trait/alias 目前不支持。如果要亲自体验可以在仓库中直接运行unavailable相关的 LIT 测试见 unavailable.mojo 的 RUN 行使用的%parse-mojo-isolated工具或阅读 unavailable_errors.mojo 了解完整的报错矩阵。【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考