news 2026/9/7 17:27:15

Rust 编译器错误码 E0087 详解:多余的泛型类型参数与 E0107 诊断机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rust 编译器错误码 E0087 详解:多余的泛型类型参数与 E0107 诊断机制

Rust 编译器错误码 E0087 详解:多余的泛型类型参数与 E0107 诊断机制

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

本文围绕 Rust 编译器历史错误码 E0087("函数被传入过多类型参数")展开,说明其触发条件、为何在当前编译器中不再被发出、以及取而代之的 E0107 错误的完整语义,并结合 rustc 源码中的泛型参数数量诊断实现,帮助读者在遇到wrong number of type arguments类报错时快速定位与修复问题。

一、E0087 的核心语义:类型参数数量必须精确匹配

错误码 E0087 的定义非常直接:给一个泛型项(函数、类型等)提供了过多的类型实参,数量超过了其声明的类型参数个数

官方错误码文档 E0087.md 给出的经典错误示例是:

fn foo<T>() {} fn main() { foo::<f64, bool>(); // error: wrong number of type arguments: // expected 1, found 2 }

fn foo<T>()只声明了 1 个类型参数T,但调用点通过 Turbofish 语法::<f64, bool>提供了 2 个类型实参。文档中的修复原则只有一句话,却抓住了本质:

The number of supplied arguments must exactly match the number of defined type parameters. (提供的实参数量必须与定义的类型参数数量完全一致。)

即调用foo的正确写法是foo::<f64>()或省略实参foo()(当类型可从上下文推断时)。

二、关键事实:E0087 已不再由编译器发出

E0087.md 的第一行就明确标注:

Note: this error code is no longer emitted by the compiler. (注意:该错误码不再由编译器发出。)

这意味着:在现代 Rust 版本中,你永远不可能再直接看到error[E0087]。编译器把"泛型实参数量不对"这一整类错误统一归并到了 E0107 之下。这一归并关系在错误码注册表中也有旁证——rustc_error_codes 的 lib.rs 中,早期错误码 E0035/E0036 被注释为merged into E0087/E0089,而 E0087 所在的"泛型参数数量"这一片区最终又进一步收敛到 E0107,形成了一条清晰的错误码演化链:

E0035 / E0036 ──(合并)──> E0087 / E0089 ──(统一归并)──> E0107 数量不足 数量过多 数量不符(统一诊断)

因此,对于维护旧代码库或阅读旧 issue 时遇到的 E0087,正确理解方式是把它当作E0107 的"历史别名":凡是历史上报 E0087 的场景,今天都会以 E0107 的形式呈现。

三、现代编译器中的实际表现:E0107

当前编译器发出的是error[E0107],消息措辞也比历史版本更精确——它不仅区分"数量过多/过少",还区分生命周期参数、类型参数、常量参数三类实参。E0107.md 汇总了完整的行为:

struct Foo<T> { x: T } struct Bar { x: Foo } // error: wrong number of type arguments: // expected 1, found 0 struct Baz<S, T> { x: Foo<S, T> } // error: wrong number of type arguments: // expected 1, found 2 fn foo<T, U>(x: T, y: U) {} fn f() {} fn main() { let x: bool = true; foo::<bool>(x); // error: expected 2, found 1 foo::<bool, i32, i32>(x, 2, 4); // error: expected 2, found 3 f::<'static>(); // error: wrong number of lifetime arguments // expected 0, found 1 }

注意第三行f::<'static>():对没有任何生命周期参数的函数提供生命周期实参,会报wrong number of lifetime arguments。这与 E0087 时代只谈"type arguments"的粗糙措辞形成了对比。

仓库中的 UI 测试可以佐证真实诊断消息的格式。tests/ui/type-alias-enum-variants/enum-variant-generic-args.stderr 中记录了编译器对类型别名变体上多余 Turbofish 实参的输出:

error[E0107]: type alias takes 0 generic arguments but 1 generic argument was supplied --> $DIR/enum-variant-generic-args.rs:64:5 | LL | AliasFixed::<()>::TSVariant(());

可以看到消息模板是<def_kind> takes <N> generic argument(s) but <M> generic argument(s) was/were supplied,并且精确标注了出错标识符的 span。

对应的正确写法(参数数量与声明一致):

struct Foo<T> { x: T } struct Bar<T> { x: Foo<T> } // ok! struct Baz<S, T> { x: Foo<S>, y: Foo<T> } // ok! fn foo<T, U>(x: T, y: U) {} fn f() {} fn main() { let x: bool = true; foo::<bool, u32>(x, 12); // ok! f(); // ok! }

四、源码级剖析:WrongNumberOfGenericArgs诊断实现

从源码结构看,E0107(即历史 E0087 场景)的完整诊断逻辑集中在 rustc_hir_analysis 的 wrong_number_of_generic_args.rs 中。理解该文件即可理解"数量不匹配"报错的每一处细节。

4.1 状态结构:一次诊断需要哪些输入

核心是WrongNumberOfGenericArgs结构体(源码 L10-L33),它把判定所需的全部上下文一次性收集齐:

字段含义
gen_params目标类型/特征期望的泛型参数(ty::Generics
gen_args用户在调用点实际提供hir::GenericArgs
gen_args_info描述缺失/冗余实参的种类与数量(见下)
angle_brackets角括号<...>的三种形态(见下)
path_segment出错的 HIR 路径段,用于定位 span
params_offset处理缺失/冗余实参时的参数索引偏移(是否含Self、生命周期参数数量等)

4.2 两类状态枚举:精确定位"缺了什么"与"多给了什么"

GenericArgsInfo(源码 L51-L86)区分四种情形:

  • MissingLifetimes { num_missing_args }:缺生命周期实参
  • ExcessLifetimes { num_redundant_args }:多余生命周期实参
  • MissingTypesOrConsts { num_missing_args, num_default_params, args_offset }:缺类型/常量实参
  • ExcessTypesOrConsts { num_redundant_args, num_default_params, args_offset, synth_provided }:多余类型/常量实参——E0087 的场景就落在ExcessTypesOrConsts这一分支上

其中num_default_params字段值得注意:由于 Rust 支持类型参数默认值(如struct Foo<T = u8>),"期望数量"存在上下界,诊断消息会相应使用 "at least N" / "at most N" 的措辞(由get_quantifier_and_bound方法生成,见 源码 L305-L325)。而synth_provided则标记用户是否显式写入了impl Trait这类编译器不允许的合成实参,诊断会追加 note:impl Trait cannot be explicitly specified as a generic argument(源码 L1141-L1147)。

AngleBrackets枚举(源码 L38-L47)则刻画调用点写法的三种形态:

  • Missing:完全没写角括号(如struct Bar { x: Foo }
  • Implied:没写角括号但存在被省略的实参(如&str中的省略生命周期)
  • Available:写了角括号但实参数量不对(如foo::<f64, bool>)——E0087 示例正是这一形态

new构造函数(源码 L89-L114)通过gen_args.span_ext().is_none()来区分这三种形态:没有 span 且实参列表为空是Missing,没有 span 但存在省略实参是Implied,否则是Available

4.3 消息与高亮:expected Nsupplied M

历史文档中的注释expected 1, found 2对应的现代实现是notify方法(源码 L546-L596):它在标识符 span 上标注expected <量化词><N> <lifetime/generic> argument(s),再对已提供的实参逐个标注,最后一个标注supplied <M> ... argument(s)

一个体现诊断细致之处的细节:当实参过多时(too_many_args_provided()),notify会提前返回、不逐个高亮实参——因为此时另有"删除冗余实参"的建议(suggest_removing_args_or_generics),二者叠加会造成标注重叠(源码 L561-L571 的注释给出了示意)。

4.4 自动修复建议:删掉多余参数

对 E0087 场景(实参过多),编译器不只报错,还会给出可应用的修复建议。suggest_removing_args_or_generics(源码 L902 起)的策略包括:

  1. 删除整个角括号:当冗余实参数 ≥ 全部实参数时,建议remove the unnecessary generics,即把foo::<f64, bool>()修复为foo()。注意 源码 L1070-L1078 中有一段专门避免 ICE 的 HACK:若同一作用域内存在同名但非泛型的类型(例如derive展开产生的重复定义),空 span 建议会触发断言,故此处显式跳过;
  2. 删除部分冗余实参:仅删除超过期望数量的那一段 span,建议文案为remove the unnecessary generic argument(s)
  3. 改写为关联项绑定:当目标是一个 trait,且未绑定的关联项数量恰好等于冗余实参数量时,编译器会建议"把多余实参改写为关联项绑定"(turn the generic argument(s) into an associated item binding(s),源码 L1010-L1051)。例如对Iterator<u32>多给一个类型参数时,可提示改为<u32 as Iterator>::Item = ...这类绑定写法;
  4. 把多余实参移到 trait 上:对关联函数调用(如Into::into::<T>(x))给出consider moving this generic argument to the Into trait的多部分建议(suggest_moving_args_from_assoc_fn_to_trait,源码 L759-L895)。

对于实参过少的场景,对应的suggest_adding_args系列方法会基于参数名甚至上下文(函数参数位推断'_、静态/常量上下文推断'static、方法调用 Turbofish 中推断_)生成add missing generic argument(s)建议(源码 L626-L751)。

4.5 最终装配:错误码 E0107 在此被写死

诊断的最终装配发生在Diagnostictrait 实现中(源码 L1150-L1168):

let msg = self.create_error_message(); let mut err = Diag::new(dcx, level, msg); err.code(E0107); err.span(self.path_segment.ident.span); self.notify(&mut err); self.suggest(&mut err); self.show_definition(&mut err); self.note_synth_provided(&mut err);

这一行err.code(E0107)从实现层面证实了第二节的结论:所有"泛型实参数量不匹配"(含历史 E0087 的"过多"场景)如今统一以 E0107 发出。此外show_definition(源码 L1094-L1138)还会在定义处追加 note,形如type defined here, with 1 generic parameter: T,把调用点与定义点关联起来。

五、遇到此类报错的排查清单

结合上述文档与源码,遇到wrong number of type/lifetime arguments(E0107)时可按以下步骤处理:

  1. 读 span 标注expected N generic argument(s)标在标识符上,supplied M标在实参上,N 与 M 的差值就是需要增删的实参数量;
  2. 对照定义:根据 note 中defined here, with ... parameter(s)定位定义,确认参数列表(注意区分生命周期参数与类型/常量参数,二者按顺序排列且各自计数);
  3. 区分过少与过多
    • 过少:补齐实参,或采用编译器建议的占位符_/ 推断(尤其是 Turbofish 中的_);
    • 过多(即历史 E0087):删除冗余实参;若目标是 trait,检查是否该写成关联项绑定或把实参挪到 trait 路径段上;
  4. 注意默认参数:带默认值的类型参数允许实参数量少于参数总数("at least N"),此时报错信息中的下界措辞来自get_quantifier_and_bound的逻辑;
  5. 旧文档迁移:若在旧博客/旧 issue 中看到 E0087,直接按 E0107 语义理解即可,二者修复方法完全一致。

六、参考资料索引

内容仓库路径
E0087 错误码文档(历史,含本文起始示例)E0087.md
E0107 错误码文档(现行,含完整正/误示例)E0107.md
错误码归并注释(E0035/E0036 → E0087/E0089)lib.rs
数量不匹配诊断的完整实现wrong_number_of_generic_args.rs
真实诊断消息样例(含 span 与消息格式)enum-variant-generic-args.stderr

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 17:25:00

Excel与WPS中REDUCE和LAMBDA递归:循环与函数式编程的盟主之争

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 17:23:21

UL 1030-2019标准解读:范围、测试与认证实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 17:22:25

Java开发必备:Git回退与origin远程联动实战指南

1. 项目概述与实战场景&#xff1a;为什么Java开发绕不开Git回退和origin联动做Java开发这么多年&#xff0c;Git几乎是每天都要打交道的工具。但说实话&#xff0c;绝大多数人只是停留在git add、git commit、git push三板斧的阶段&#xff0c;一旦遇到代码回退、远程分支同步…

作者头像 李华
网站建设 2026/9/7 17:22:15

小程序码生成与scene参数解析:从接口调用到渠道归因实战

做小程序推广的同学应该都遇到过这类需求&#xff1a;给每个渠道生成专属的小程序码&#xff0c;用户扫进来之后&#xff0c;后台要能区分出这个用户是从哪个渠道来的&#xff1b;做分销裂变的项目&#xff0c;需要知道这个分享动作是谁发起的&#xff0c;业绩算在谁头上&#…

作者头像 李华
网站建设 2026/9/7 17:21:04

开发工具全解析:从IDEA、跨端框架到调试与离线方案

1. 开发工具全景图&#xff1a;先把“用什么”和“为什么用”搞清楚如果你去问一个刚入行的开发者&#xff0c;开发工具是什么&#xff0c;他大概率会回答“就是写代码的软件呗”。这话没错&#xff0c;但只对了一小半。真正的开发工具是一个完整的工具箱——从你写下第一行代码…

作者头像 李华