Rust 编译器 E0057:闭包与 Fn 系 trait 调用参数个数不匹配错误的完整解析
Rust 编译器 E0057闭包与 Fn 系 trait 调用参数个数不匹配错误的完整解析【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rustE0057 是 Rust 编译器rustc中专门用于以调用语法调用闭包或Fn/FnMut/FnOncetrait 实现时传入实参个数与形参声明不一致这一错误的错误码。本文以 rustc 仓库中的官方错误码文档 E0057.md 为主体结合类型检查阶段rustc_hir_typeck的源码实现讲清该错误的触发条件、实际报错格式、编译器内部的检查机制以及如何修复此类错误。读完本文你将能够独立定位 E0057 报错的根因并理解 rustc 是如何在类型检查期完成实参个数匹配判断的。一、错误场景E0057 何时触发官方文档给出的原始定义是一句话An invalid number of arguments was given when calling a closure.调用闭包时给出了无效的参数个数。文档中附带的示例是最小复现场景该代码块以compile_fail,E0057标注即它预期编译失败并产出 E0057会作为 rustc 自身的回归测试持续验证let f |x| x * 3; let a f(); // 非法参数过少 let b f(4); // 这样调用没问题 let c f(2, 3); // 非法参数过多闭包|x| x * 3声明了 1 个参数因此f()传入 0 个实参 → 触发 E0057参数过少f(4)传入 1 个实参 → 合法f(2, 3)传入 2 个实参 → 触发 E0057参数过多。需要强调一点E0057 是双向错误无论是少传还是多传只要实参个数不等于形参个数都会命中该错误码。泛型函数同样适用官方文档特别指出以泛型参数接收闭包时规则完全一致fn fooF: Fn()(f: F) { f(); // 这样调用合法但 f(3) 不行 }这里F: Fn()表明f必须实现零参数的Fntrait。在函数体内调用f()是合法的如果写成f(3)编译器会以 E0057 报错——因为约束声明的参数个数是 0实参个数必须与之精确匹配。这条规则同样适用于任何带Fn、FnMut、FnOnce约束的泛型参数实参个数必须与约束签名中的参数列表一致。实际报错长什么样当 E0057 被触发时编译器输出的诊断信息形如以f()少参为例error[E0057]: this closure takes 1 argument but 0 arguments were supplied这条takes N arguments but M arguments were supplied的措辞正是 rustc 源码中拼接出来的诊断模板下一节将展示它的确切出处。二、错误码文档在 rustc 仓库中的组织方式在深入原理前先说明 E0057 文档本身在仓库中的位置和维护约定这解释了为什么错误码文档长这样每个错误码的说明文档独立存放在compiler/rustc_error_codes/src/error_codes/EXXXX.md下E0057 对应 E0057.md错误码清单以宏形式集中登记在 rustc_error_codes/src/lib.rs 的error_codes!宏中其中包含0057条目。该文件明确约定即使某个错误码不再被编译器发出也绝不允许从宏列表中删除只需在对应 markdown 文件中注明不再发出、并把已无法构建的代码示例标记为ignore (no longer emitted)。这一约定保证了错误码数字永不复用使得任何历史编译日志中的 E 编号都能追溯回稳定含义文档格式须遵循 RFC 1567错误码长说明规范化并且error_codes!宏的内容会被 tidy 工具check_error_codes_docs校验保证文档与宏列表一致。因此 E0057 文档中那两段代码块不仅是示例还是持续执行的回归测试compile_fail,E0057表示该示例必须编译失败且失败原因必须恰好是 E0057。三、源码实现rustc 如何完成参数个数检查E0057 的判定发生在类型检查阶段type checking具体实现在 compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs。核心逻辑可以拆成计数预判 → 逐参类型匹配 → 诊断构建三步。3.1 第一道门槛实参个数是否可能满足在逐参数做类型检查之前编译器先做一个纯计数的预判决定这次调用有没有可能成功// if the wrong number of arguments were supplied, we CANT be satisfied, // and if were c_variadic, the supplied arguments must be the minimum count from the function // otherwise, they need to be identical, because rust doesnt currently support variadic functions let mut call_appears_satisfied if c_variadic { provided_arg_count minimum_input_count } else { provided_arg_count minimum_input_count };这段代码位于 checks.rs 约 L425-L429体现了 E0057 判定规则的两个要点普通函数/闭包provided_arg_count minimum_input_count实参个数必须与形参个数精确相等。Rust 目前没有可变参数函数C 风格变长参数c_variadic除外所以不存在个数不同但类型上可以凑合的情况C 变长函数如 FFI 引入的c_variadic函数要求实参个数 ≥ 声明的最少形参个数此时个数不足走的是另一个错误码 E0060可参考 E0060.md而非常规路径。一旦call_appears_satisfied为false编译器就会把这次调用送入错误上报路径report_arg_errorschecks.rs 约 L859 起最终由FnCallDiagCtxt构建诊断。3.2 诊断构建E0057 的文案从哪来report_arg_errors内部会区分三类情况缺少/多余/参数换位missing/extra/swapped arguments参数个数正确但类型不匹配无效参数如循环类型等。关键的诊断构建逻辑在initial_final_diagnostic方法中checks.rs 约 L2544-L2568fn initial_final_diagnostic(self) - Diag_ { if self.formal_and_expected_inputs.len() self.provided_args.len() { struct_span_code_err!( self.dcx(), self.call_metadata.full_call_span, E0308, arguments to this {} are incorrect, self.call_metadata.call_name, ) } else { self.arg_matching_ctxt .dcx() .struct_span_err( self.call_metadata.full_call_span, format!( this {} takes {}{} but {} {} supplied, self.call_metadata.call_name, if self.arg_matching_ctxt.args_ctxt.c_variadic { at least } else { }, potentially_plural_count(self.formal_and_expected_inputs.len(), argument), potentially_plural_count(self.provided_args.len(), argument), pluralize!(was, self.provided_args.len()) ), ) .with_code(self.err_code.to_owned()) } }从源码结构可以看出两个值得注意的设计个数相等但类型错误时走E0308类型不匹配分支个数不等时才走 else 分支拼接出this closure/function takes N argument(s) but M argument(s) was/were supplied的文案再通过.with_code(self.err_code.to_owned())挂上此前记录的错误码普通调用即为 E0057C 变长不足时为 E0060也就是说takes ... but ... supplied 这句文案是 E0057 与 E0060 共用的模板错误码在调用检查的早期就已决定诊断阶段只是盖章。此外当多传一个参数且去掉它之后类型全部成立这一常见场景时report_arg_errors还会调用maybe_optimize_extra_arg_suggestion()checks.rs 约 L933来替换成更友好的删掉多余参数修复建议帮助开发者一步修复 E0057。3.3 新特性splat参数展开路径也复用 E0057在 checks.rs 中还有一条较新的检查路径——针对rustc_splat属性函数参数展开特性尚未稳定的调用检查。源码中有两处直接引用 E0057并带有明确的 FIXME 注释// FIXME(splat): update the error code E0057 docs when splat is stabilized if Some(detup_formal_arg_tys.len()) ! tupled_args_count { err_code Some(E0057); }以及稍后的另一处checks.rs 约 L795-L806} else if formal_input_tys.len() ! provided_args.len() { // FIXME(splat): suggest alternative argument counts, if there are any let guar struct_span_code_err!( self.dcx(), call_span, E0057, this splatted function takes {} arguments, but {} {} provided, formal_input_tys.len(), provided_args.len(), if provided_args.len() 1 { was } else { were }, ) .emit();可以推断随着 splat 特性走向稳定E0057 的官方文档需要扩充以覆盖展开调用splatted call场景——源码中的 FIXME 注释明确承诺了这一维护事项。这也解释了为什么官方文档目前只覆盖了经典闭包场景。四、为什么闭包、Fn/FnMut/FnOnce 都走同一套检查文档提到当通过调用语法调用闭包或其他Fn、FnMut、FnOnce实现时参数个数必须与定义匹配。从实现视角看这是因为 Rust 中所有调用语法f(...)最终都被解析为对应的函数 trait 的call方法调用闭包表达式|x| ...自动实现Fn/FnMut/FnOnce之一f(args)的元组参数tuple argument会被解包与 trait 签名声明的参数列表逐一比对。因此 rustc 在check_expr处理ExprKind::Call时对函数项调用与闭包调用采用同一套形参/实参比对流程——这就是为什么 E0057 的文档标题虽然说的是calling a closure但同样适用于任何Fn系实现。文档后半段的泛型示例fn fooF: Fn()(f: F)正是这一机制的直接体现编译器依据 trait 约束推导出f的调用签名再按同一规则校验实参个数。五、修复 E0057 的实操清单核对形参个数打开被调用闭包/函数的定义数一下参数列表长度。对闭包|a, b, c| ...来说必须传入恰好 3 个实参对Fn()约束则必须传入 0 个。区分少传与多传报错文案this closure takes N arguments but M arguments were supplied中N 是期望值、M 是你实际提供的个数对照二者即可定位是漏传还是多传若多传的只是最后一个参数编译器通常还会给出删除该参数的直接修复建议。泛型约束要一起看如果错误出现在泛型函数体内如fn fooF: Fn(x)(f: F)中的f(...)请对照 trait 约束签名修改调用而不是猜测实现类型。变长函数是例外C 风格变长函数c_variadic允许至少传入声明的参数个数此时太少报 E0060 而非 E0057太多是允许的。验证方式修复后直接cargo build/cargo check即可若想确认某段示例确实应产生 E0057可参考官方文档中以compile_fail,E0057标注的测试代码块见 E0057.mdrustc 仓库的测试基础设施会持续校验这一行为。六、小结E0057 是一条边界非常清晰的错误码只要调用语法(...)传递的实参个数与目标闭包或Fn/FnMut/FnOnce实现声明的参数个数不相等即触发该错误。其判定逻辑在 checks.rs 的实参匹配流程中分两步完成——先做精确计数预判provided_arg_count minimum_input_count再在诊断构建阶段拼接 takes N arguments but M arguments were supplied 文案并挂上错误码。错误码本身则在 rustc_error_codes 中以永不删除、永不复用的约定集中登记并配有遵循 RFC 1567 的长说明文档。理解这条链路后你在 rustc 源码中遇到任何takes ... but ... supplied形式的报错都能快速回溯到参数个数检查这一核心位置。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考