设计解析)
Carbon 语言未使用模式绑定unused关键字与_通配符设计解析【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang本文基于 Carbon Language 仓库中的提案 p002022-unused-pattern-bindings-unused-function-parameters.md 展开系统讲解 Carbon 如何让开发者显式声明未使用的模式绑定unused pattern binding即如何表达未使用的函数参数。文章完整继承提案的语法设计、行为语义与备选方案讨论并结合当前仓库中词法分析、语义检查与诊断输出的真实实现帮助读者理解_: i32与unused size: i32两种写法背后的设计动机、编译期检查规则及在函数声明/变量解构/match 分支中的实战用法。问题背景为什么要显式声明未使用如何在 Carbon 中声明一个未使用的模式绑定是 p002022 要回答的核心问题。由于函数参数声明是模式pattern的一种特定形式这个问题更常见的等价表述是如何声明一个未使用的函数参数让绑定可以被显式标记为未使用可以带来两方面收益代码意图明确、无歧义作者可以清晰地向读者声明这个值不打算被使用避免他人误以为漏写了逻辑工具链可精确处理编译器或 linter 既能发现标记为未使用却被使用的参数也能发现没有标记未使用却从未被使用的参数两种误用都可通过诊断信息被捕获。从设计脉络看这一提案也承接了早期讨论仓库 docs/design 中关于命名绑定与下划线字符_的整体设计以及此前 issue #476 Optional argument names (unused arguments) 的讨论。此外提案还对其他语言生态的既有做法做了调研详见既有语言实践一节为最终语法选择提供了参照。提案核心两种显式声明未使用的方式提案引入两种语法让作者在简洁与显式之间自行取舍短形式用下划线 token_代替名字例如_: i32长形式在名字前加unused关键字例如unused size: i32。两种形式对人类读者、代码作者以及程序化解析都是无歧义的。unused关键字的价值在于作者可以保留名字用于文档表达同时仍然以机器可解释的方式标记该值被丢弃。而_单独出现时在 Carbon 中只有单一含义不会因上下文不同产生歧义。从实现角度看unused已作为正式关键字进入词法层。在仓库 toolchain/lex/token_kind.def 中可以看到CARBON_KEYWORD_TOKEN(Unused, unused)CARBON_KEYWORD_TOKEN表明它被登记为 Carbon 的关键字 token后续的词法lex、语法parse与语义检查check各阶段都围绕这一 token 展开。unused关键字的语义约束提案对unused关键字作了两点明确约束只能在模式绑定中位于名字之前时才合法与后面的名字紧密绑定不允许把一个完整的子模式sub-pattern整体标记为unused。也就是说可以写unused size: i32绑定单个名字但不能写类似unused (a: i32, b: i32)这种作用于整个子模式的写法。unused绑定与_绑定的行为语义标记为unused的名字可见但不可用提案规定带unused限定的名字对名字查找name lookup是可见的但任何使用都是非法的即使这种使用会引发歧义的名字查找错误也不例外。一旦尝试使用编译器将报错并提示用户要么移除unused限定符要么移除该使用。这一语义在当前源码中得到了精确实现。在 toolchain/check/handle_binding_pattern.cpp 中语义检查阶段通过MarkPatternUnused遍历模式中的所有绑定将name.is_unused置位其中对_做了特别处理——_本身就被视为隐式未使用因此对不含任何绑定的模式加unused会触发UnusedPatternNoBindings警告unusedmodifier on pattern without bindings而对纯_标记unused是冗余但无害的。随后toolchain/check/unused.cpp 中的CheckUnusedBinding集中实现双向诊断if (entity_name.is_unused) { if (result.use_loc_id.has_value()) { CARBON_DIAGNOSTIC_ON_SCOPE(UnusedButUsed, Error, variable {0} marked unused but used, SemIR::NameId); CARBON_DIAGNOSTIC(UnusedButUsedHere, Note, usage here); ... } } else { if (!result.use_loc_id.has_value() result.is_decl_reachable) { CARBON_DIAGNOSTIC_ON_SCOPE(UnusedBinding, Warning, binding {0} unused, SemIR::NameId); ... } }这段实现与提案描述一一对应已标记unused却仍被使用→ 报UnusedButUsed错误Error并附带UnusedButUsedHere的 note 指出使用位置未标记unused却从未被使用→ 发UnusedBinding警告Warning提示用户要么删除绑定、要么标记为未使用。同时实现中还包含几个细致的安全边界这些是提案未展开、但实现层必须处理的细节特殊名字_与.Self不会被警告——_天然未使用.Self常为隐式出现来自其他文件ImportIRInstId的名字不警告避免对导入实体误报只有当名字在可达位置is_decl_reachable声明时才会警告若某名字在不可达代码中被使用则不会警告因为把名字改成_反而会在不可达代码中引发名字查找错误参见 toolchain/check/unused.h 中的注释。这些诊断种类也登记在 toolchain/diagnostics/kind.defUnusedBinding中并可通过仓库中的测试文件验证例如 toolchain/check/testdata/patterns/unused.carbon 就包含大量针对unused绑定、重声明一致性等的 CHECK 测试。完整示例三种典型场景提案给出了三类最具代表性的使用场景下面完整收录并稍作注解。场景一未使用的函数参数函数声明可能位于 API 文件中带命名参数size而实现中并不使用它// 函数声明可位于 API 文件中 fn Sum(x: List(i32), size: i32) - i32; // 不使用 size 参数的实现 fn Sum(x: List(i32), _: i32) - i32 { ... } // 或 fn Sum(x: List(i32), unused size: i32) - i32 { ... }短形式_: i32适合不关心参数名、只想满足签名约束的场景长形式unused size: i32则保留了参数名size作为文档说明这个参数本意是 size但当前实现暂未使用。场景二未使用的解构变量从元组返回值中只解构出需要的部分fn Bar() - (i32, i32); fn Foo() - i32 { var (x: i32, _: i32) Bar(); // 或 var (x: i32, unused y: i32) Bar(); return x; }场景三未使用的 match 分支绑定在match的模式匹配中丢弃不需要的分量fn Bar() - (i32, i32); fn Foo() - i32 { match (Bar()) { case (42, y: i32) { return y; } case (x: i32, _: i32) { return x; } // 或 case (x: i32, unused y: i32) { return x; } } }注意unused需要标记的是模式中具体某个名字绑定而非整个 case 模式在本例中x被使用、y被丢弃因此只需对y加unused或用_替代。既有语言实践_的跨语言先例提案对现有语言生态做了简要调研作者声明这只是概览而非穷尽为选择_作为语义符号提供依据C/C 生态C Core Guidelines 对未使用参数有专门建议Google C 风格指南也对函数声明/定义中的未使用参数有约定C 提供[[maybe_unused]]属性GCC 提供 C 语言的unused函数属性。这些方案对读者而言清晰度参差不齐。以_表达丢弃的语言Rust 的_模式忽略模式中的值、Golang 的空白标识符blank identifier、PyLint 允许以_、ignored_、unused_开头的参数不被使用、Scala 的通配符模式、Crystal 的_case。Carbon 的结论是单独的下划线字符在 Carbon 中只有未使用值这一个含义可以保证跨上下文的无歧义性这与 Rust、Go 等语言的既有心智一致降低学习成本。设计合理性论证Rationale提案的取舍服务于 Carbon 的总体目标——代码易于阅读、理解与编写见 docs/project/goals.md具体体现在三点原则避免难以输入、难以看清、难以与相似符号区分的符号_是键盘上易输入、视觉上清晰可辨的字符语法应能被任何人在任何开发环境中轻松解析与扫描不依赖 IDE 的语义提示纯文本即可读懂显式性要与简洁性平衡啰嗦与仪式感会给读者增加认知负担而显式性则减少读者需要掌握的外部上下文。两种语法并存恰好覆盖这条光谱的两端_提供极简的丢弃写法unused size: i32让代码读起来自然、保留名字语义。作者可以在追求简洁与追求显式之间按需选择。备选方案回顾Alternatives Considered提案逐一审视了五种备选方案并说明放弃理由这些讨论对理解最终设计很有价值。备选 1注释化的名字Commented NamesC 允许函数参数匿名从而写出int Foo(int) { ... } int Foo(int /*unused_name*/) { ... }优点与 C 一致。缺点Carbon 不打算支持/* */块注释因此该方案从一开始就不可行。备选 2仅支持_短形式只提供_这一种丢弃语法。优点语言更小少一个关键字只有一种写法。缺点表达能力弱无法通过名字提供文档信息无法解释这个被丢弃的值本应是什么。备选 3以_开头的命名标识符将_size这类前缀下划线的标识符视为未使用标识符并丢弃其名字。优点短、长形式都复用了下划线字符功能上接近 C 的注释化名字。缺点这是提案反对最强烈的方案理由有三把名字未使用这一语义与标识符的拼写以下划线开头绑定过于隐晦、间接不应把标识符拼写当作语义信息的侧信道side-channel是否使用这一语义应与名字正交地表达而不是与名字耦合当变量在被使用与未被使用之间切换时必须改名这会破坏其在注解、元编程或诊断中的引用价值。备选 4匿名的具名标识符_ size形式在下划线 token 后跟一个可选名字后缀_ size: i32。优点长短形式都复用下划线 token。缺点虽然一致但下划线 token 相比unused这样的专门关键字人类可读性稍差。备选 5属性AttributesCarbon 未来可能引入属性机制来附加元数据届时也可设计unused属性实现类似语义。优点由属性生态统一承载这类标注用通用机制解决具体问题无需引入新关键字。缺点在当前提案中unused绑定规定了编译器处理名字的无歧义、绝对行为类似于private关键字用属性作为该语义的载体过于间接。且属性尚未设计方向未定提案建议待属性完全设计后再重新评估此选项。小结Carbon 通过_短形式 unused关键字长形式两种互补语法解决了未使用模式绑定尤其是未使用函数参数的声明问题。_提供简洁的丢弃表达unused name在保留名字文档价值的同时显式标记丢弃编译器的双向诊断UnusedButUsed错误与UnusedBinding警告确保标记与使用状态始终一致。当前仓库的词法toolchain/lex/token_kind.def、语义检查toolchain/check/handle_binding_pattern.cpp、toolchain/check/unused.cpp与测试toolchain/check/testdata/patterns/unused.carbon均已落实该设计是理解 Carbon 模式系统与诊断机制的良好入口。【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考