ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Carbon 语言 `for` 循环设计解析:从提案 p000353 到当前语法与源码实现

Carbon 语言 `for` 循环设计解析:从提案 p000353 到当前语法与源码实现 Carbon 语言for循环设计解析从提案 p000353 到当前语法与源码实现【免费下载链接】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 语言早期设计提案 p000353-for-loops.md 为核心系统梳理 Carbon 中for循环的语法来源、设计决策与演进过程并结合当前仓库中 loops.md、iterate.carbon、range.carbon 以及解析器/检查器源码讲清为什么只保留 range-basedfor、拒绝 semisemifor这一关键取舍以及最终语法for (pattern in expression)在编译器中的落地路径。读完本文你将掌握 Carbon 循环语法的设计脉络、与 C 的异同以及Iterate接口如何支撑自定义类型的遍历。背景为什么 Carbon 需要for循环提案开篇即指出控制流是语言的基础设施参见 语言概述而for循环在 C 中极其常见Carbon 作为面向 C 迁移者的语言必须提供某种形式的for。提案逐一调研了主流语言的for形态这些对比构成了后续设计决策的依据C存在两种形态。传统semisemi写法for (int i 0; i list.size(); i)源自 C 语言而range-based写法for (auto x : list)是 C11 新增。range-based 更简洁但拿到多个信息如同时要索引时反而麻烦int i 0; for (const auto x : list) { printf(List at %d: %s\n, i, x.name); i; }Java与 C 等价虽无逗号运算符但 semisemi 的首尾区段支持逗号分隔的多条语句。TypeScript / JavaScript提供三种形态——semisemi同 C、for (x of list)同 range-based、for (x in list)返回索引。Python / Swift / Rust只支持 range-based统一使用for x in list语法。Go以for为唯一循环构造包含 semisemi、仅条件的for i list.size()等价 Cwhile、以及无限循环for {。结论很清晰主流语言都在向 range-based 形态收敛这为 Carbon 的取舍提供了参照。提案核心采用 C 风格的 range-basedfor提案 p000353-for-loops.md 的核心结论是Carbon 应采纳 C 风格的 range-basedfor语法semisemifor交由其他机制解决不进入语言。与之配套的三个关键字及其语义与 C/C 一致for发起循环continue跳过循环体其余语句直接进入下一次迭代break立即退出循环。提案给出的初始语法为for ( var 类型 变量 : 表达式 ) { 语句 }例如for (var x: T in list) { ... }。与大括号可选且须成对出现{ ... }的规则一致与 if/else 提案 保持同步不带大括号时循环体只能有一条语句。语法演进从:到in值得注意提案最初写的是for (pattern : expression)而当前语言最终采用的是in。演进依据可见 loops.md 的现行语法for (patterninexpression) {statements}这一变化由后续提案 p000618-var-ordering.md:与in之辨敲定。原因在 p000353-for-loops.md 的备选方案章节早有伏笔若var声明语法与for头都使用:可能存在语法歧义风险由于var提案最终未采用:改用in既消除了潜在歧义也与 Python/Swift/Rust 等语言的直觉一致。编译器层面的证据更加直接在解析器 handle_statement.cpp 中遇到:会发出诊断 :should be replaced byinExpectedInNotColon即当前编译器将:视为错误写法并提示改用in。这意味着in已是 Carbon 的唯一合法分隔符。当前语法还明确了绑定语义模式默认按值不可变绑定与函数参数、其他模式匹配上下文一致等同let可通过var关键字改为可变绑定但修改不会影响底层容器for (name: strbuf in names) { Print(name); } for (var name: strbuf in names) { // name 可修改但不会改动 names 容器本身。 }in右侧的临时对象在循环执行期间保持存活避免悬垂内存访问。范围输入支持哪些可遍历对象提案明确指出虽然 range 输入的具体语法不在本提案范围内但必须支撑三类对象可与 C range-basedfor互操作的 C 对象如带迭代器的容器Carbon 数组及其他容器范围字面量range literals——本提案未正式提出仅举例说明其他语言中0..2可表示整数集合 [0, 2)。这三条如今都已落地分别对应Iterate接口及其对 C range 的适配见下节iterate.carbon 中为array(T, N)提供的Iterate实现range.carbon 中的IntRange、Range(end)半开区间 [0, end)与InclusiveRange(start, end)闭区间工具。底层机制Iterate接口与 C 互操作用户自定义类型要让for循环可用只需实现Iterate接口。当前 iterate.carbon 的定义为interface Iterate { let ElementType: Copy Destroy; let CursorType: Destroy; fn NewCursor(self) - CursorType; fn Next(self, cursor: CursorType*) - Optional(ElementType); }游标cursor跟踪遍历进度Next推进游标并返回Optional(ElementType)空Optional表示到达末尾。for循环在概念上等价于参考 loops.md其中Optional的 API 当时尚未最终批准var cursor: range.(Iterate.CursorType) range.(Iterate.NewCursor)(); var iter: Optional(range.(Iterate.ElementType)) range.(Iterate.Next)(cursor); while (iter.HasValue()) { ExecuteForBlock(iter.Get()); iter container.(Iterate.Next)(ref cursor); }这个游标 Optional模型在检查器 handle_loop_statement.cpp 中被逐字实现先调用Iterate.NewCursor初始化游标变量在循环头调用range.(Iterate.Next)(cursor)随后用HasValue()作为条件分支到循环体再用Get()取出元素进行模式匹配LocalPatternMatch。而可与 C 互操作并非空话同一文件 iterate.carbon 定义了私有的CppRangeForIterate、CppIterator、CppSentinelFor、CppRange约束为满足Begin/End、可自增、可解引用等 C 迭代器约定的类型提供了Iterate的 blanket 实现以(Iterator, Sentinel)二元组为游标。测试 range_for.carbon 中大量for (i: i32 in r)形态的用例正是对 C 容器互操作路径的验证。拒绝 semisemifor一段经典的continue语义分析提案最具技术含量之处是为什么不提供 semisemifor的论证。核心问题在于semisemifor的第三区段在每次迭代末尾求值这一时机与continue交互微妙很难用while等价改写。提案给出了四段对比代码p000353-for-loops.mdfor (int i 0; i 3; i) { if (i 1) continue; printf(%d\n, i); } int j 0; while (j 3) { if (j 1) continue; printf(%d\n, j); j; } int k 0; while (k 3) { k; if (k 1) continue; printf(%d\n, k); } int l 0; while (l 3) { if (l 1) { l; continue; } printf(%d\n, l); l; }四段代码行为迥异第一段semisemi打印0和2第二段while中递增放最后遇到continue后递增永不执行无限循环第三段递增提前只打印2第四段是唯一与第一段等价的写法但复制了递增语句丑陋且易错。结论while中没有一个天然的位置放置递增。这是 semisemifor的隐藏价值也是其被否决前必须回答的问题。支持 semisemi 的论点迁移便利、更灵活与反对论点易出错、一旦加入极难移除、代码会快速堆积相权衡后提案选择拒绝 semisemi其依据是 Carbon 的两条目标目标 3代码易读、易懂、易写——semisemi 语法复杂易错range-based 写法更安全即使要提供等价结构也必须比 semisemi 更好理解目标 7与现有 C 代码互操作与迁移——语法贴近 C 便于开发者过渡。作为补偿方案提案设想了两条迁移路径一是编译器对 semisemi 写法给出指导性错误建议改用 preferred 结构二是依赖后续的 range 字面量等提案提供强力的替代循环结构。若未来 Carbon 始终未能演化出更好的方案semisemi 支持仍会补入——这是一个有条件地推迟而非永久否决的决定。其他备选方案回顾除 semisemi 外提案还评估了两项备选用in替代:已在本文第三节说明最终被采纳为现行语法理由是规避未来var与for的分隔符歧义风险且不与 C 基线冲突多变量绑定for (auto [x, y] : range_of_pairs)C 允许结构化解构提案认为 Carbon 可借由元组天然支持为此引入专用for语法是冗余的。从当前测试 pattern.carbon 中的for ((a: bool, b: bool) in ...)看模式解构路径已实际可行。当前实现状态解析器与检查器中的forfor循环如今在 Carbon 工具链中已完整实现可从源码追踪其生命周期解析阶段handle_statement.cpp按状态机顺序处理StatementForHeader消费(并进入模式解析 →StatementForHeaderIn要求in关键字否则报ExpectedIn/ExpectedInNotColon→StatementForHeaderFinish消费)生成ForHeader节点 → 循环体解析完毕后由HandleStatementForFinish产出ForStatement节点。语义检查阶段handle_loop_statement.cpp负责为 range 与游标创建作用域 → 调用Iterate.NewCursor创建游标变量 → 开启循环头基本块 → 调用Iterate.Next获取Optional元素 → 以HasValue()分支进入循环体或退出 → 用Get()初始化循环模式。break/continue则在 同一文件 中通过break_continue_stack跳转到循环退出块/循环头且越界使用break/continue不在循环内会触发BreakOutsideLoop/ContinueOutsideLoop诊断。测试验证basic.carbon覆盖了失败路径对未实现Iterate的类型遍历报MissingImplInMemberAccess与成功路径通过 SemIR dump 断言NewCursor、Next、HasValue、Get的完整降级过程。小结从提案 p000353 到今天Carbon 的for循环走过了一条设计—取舍—落地的完整路径语法从for (var x: T : expr)演进为for (pattern in expr)以in分隔并默认按值绑定能力通过Iterate接口统一了数组、IntRange范围与 C 容器的遍历取舍上坚持拒绝 semisemifor以while range-based 覆盖循环需求并把对迁移故事的补救寄望于后续提案实现上解析器与检查器严格照应提案与设计文档测试数据翔实可查。对于希望深入了解 Carbon 循环语义的读者建议按此顺序阅读proposals/p000353-for-loops.md本文依据、docs/design/control_flow/loops.md现行设计、core/prelude/iterate.carbon 与 core/prelude/range.carbon接口与范围实现最后对照 handle_loop_statement.cpp 与 basic.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),仅供参考
返回列表