为何Rust中易错函数泛型结果会被推断为()而非歧义?
Rust类型推断:为何
make_t()会默认选择()而非判定类型歧义? 在下面的Rust代码中,make_t()函数返回泛型T的Result类型,错误传播后的值通过From trait实现创建MyType。按预期此处类型应该存在歧义(比如String或()等),但编译器却无报错地选择了()对应的From实现。移除From<()>实现时,编译器会报错要求该实现;而将make_t()改为非易错函数时,又会出现类型推断歧义。为什么编译器会选择()而非判定类型歧义?
代码示例
struct Error; struct MyType; impl From<String> for MyType { fn from(_: String) -> Self { println!("used From<String>"); Self } } impl From<()> for MyType { fn from(_: ()) -> Self { println!("used From<()>"); Self } } fn make_t<T: Default>() -> Result<T, Error> { Ok(T::default()) } impl MyType { fn new() -> Result<Self, Error> { Ok(MyType::from(make_t()?)) } } fn main() { let _ = MyType::new(); }
运行输出
used From<()>
移除From<()>后的错误信息
error[E0277]: the trait bound `MyType: From<()>` is not satisfied --> src/main.rs:17:25 | 17 | Ok(MyType::from(make_t()?)) | ------------ ^^^^^^^^^ the trait `From<()>` is not implemented for `MyType` | | | required by a bound introduced by this call | = help: the trait `From<String>` is implemented for `MyType`
将make_t()改为非易错后的错误信息
修改后的make_t()函数:
fn make_t<T: Default>() -> T { T::default() }
对应的错误信息:
error[E0283]: type annotations needed --> src/main.rs:24:25 | 24 | Ok(MyType::from(make_t())) | ------------ ^^^^^^ cannot infer type of the type parameter `T` declared on the function `make_t` | | | required by a bound introduced by this call | note: multiple `impl`s satisfying `MyType: From<_>` found --> src/main.rs:4:1 | 4 | impl From<String> for MyType { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... 11 | impl From<()> for MyType { | ^^^^^^^^^^^^^^^^^^^^^^^^ = note: and more `impl`s found in the following crates: `core`: - impl<T> From<!> for T; - impl<T> From<T> for T; help: consider specifying the generic argument | 24 | Ok(MyType::from(make_t::<T>())) | +++++
原因分析
这是Rust类型推断的默认类型规则和上下文优先级共同作用的结果:
- 当使用
?操作符时,编译器会先处理Result的错误分支,对于成功分支的类型推断,会优先选择最基础、最具体的默认类型。()是Rust类型推断中的典型"兜底"类型,同时它满足make_t()的Default约束(()的Default实现直接返回()),也实现了From<()> for MyType,因此被编译器优先选中。 String虽然也满足From<String> for MyType,但它的Default实现(返回空字符串)属于更复杂的类型,编译器在无显式标注时不会优先选择。- 当
make_t()改为非易错函数后,类型推断的上下文变了:此时没有?带来的错误分支处理流程,编译器需要从MyType::from的多个impl中反向推导T的类型,而多个impl都符合条件,因此会触发类型歧义错误,要求显式指定类型。
使用turbofish语法(如make_t::<String>())可以显式指定类型,避免编译器默认选择()。
内容的提问来源于stack exchange,提问作者kmdreko
相关产品推荐
相关产品推荐

