为何限制未使用泛型会引发Rust编译错误?
我不理解为何限制未使用的泛型会导致编译错误。为何以下代码可以编译:
#[derive(PartialEq)] struct B; fn is_eq<T>(b: B, c: B) -> bool // where // B: PartialEq<T>, { b == c }
但取消where子句的注释后会出现如下编译错误:
error[E0308]: mismatched types --> src/lib.rs:9:10 | 4 | fn is_eq<T>(b: B, c: B) -> bool | - this type parameter ... 8 | b == c | ^ expected type parameter `T`, found `B` | = note: expected type parameter `T` found struct `B`
显式调用PartialEq::<B>::eq(&b, &c)可以通过编译——但编译器难道不应该自动选择该实现吗?奇怪的是,将输入参数改为&B并使用==也能编译。希望了解错误产生的原因及背后的逻辑。
核心原因:泛型约束的优先级与类型推断规则
1. 无约束时的类型推断逻辑
当没有where B: PartialEq<T>约束时,编译器会根据b == c的调用场景自动推导:因为b和c都是B类型,所以会匹配B自己实现的PartialEq<B>(由#[derive(PartialEq)]自动生成),完全不需要用到泛型参数T——此时T是一个未使用的泛型,编译器只会把它当成“占位符”,不会影响实际的类型推导。
2. 添加约束后的优先级变化
一旦添加了B: PartialEq<T>的约束,编译器的逻辑就变了:它会认为你明确指定了当前函数中B的PartialEq实现应该针对T。此时b == c的语法糖会被展开为PartialEq::eq(&b, &c),而编译器会优先尝试使用你指定的PartialEq<T>实现,而不是默认的PartialEq<B>。
但问题在于,eq方法的第二个参数需要是&T类型,而你传入的是&B,类型不匹配,所以直接报错。
3. 显式调用能编译的原因
当你写PartialEq::<B>::eq(&b, &c)时,相当于手动指定了PartialEq的泛型参数是B,直接绕过了where子句中指定的PartialEq<T>约束——编译器会优先使用你显式指定的实现,而不是函数约束里的。
4. 参数改为&B能编译的原因
这是因为引用类型的PartialEq实现是自动生成的:对于任意类型U,如果U: PartialEq<V>,那么&U: PartialEq<&V>。当你把参数改成&B,此时==调用的是&B的PartialEq实现,而你的where约束是B: PartialEq<T>,编译器可以推导出&B: PartialEq<&T>,但此时你传入的两个参数都是&B,编译器会进一步推导T = B,刚好满足约束,所以不会报错。
内容的提问来源于stack exchange,提问作者mildog8

