我可以避免使用泛型来解决 trait 实现的急切歧义吗? [英] Can I avoid eager ambiguity resolution for trait implementations with generics?
问题描述
考虑以下 Rust 代码 [游乐场]:
Consider the following Rust code [playground]:
use std::collections::HashMap;
use std::hash::Hash;
trait Foo<K> {
const FOO: i32;
}
impl<K, K_, V> Foo<HashMap<K_, V>> for HashMap<K, V>
where
K: Hash + Eq + Into<K_>,
{
const FOO: i32 = 1;
}
impl<K, V, V_> Foo<HashMap<K, V_>> for HashMap<K, V>
where
K: Hash + Eq,
V: Into<V_>,
{
const FOO: i32 = 2;
}
fn main() {}
(const
不相关,我希望代码也能用 fn
s 编译).
(The const
is not relevant, I'd like the code to compile with fn
s too).
编译失败,报错:
error[E0119]: conflicting implementations of trait `Foo<std::collections::HashMap<_, _>>` for type `std::collections::HashMap<_, _>`:
--> src/main.rs:15:1
|
8 | / impl<K, K_, V> Foo<HashMap<K_, V>> for HashMap<K, V>
9 | | where
10 | | K: Hash + Eq + Into<K_>,
11 | | {
12 | | const FOO: i32 = 1;
13 | | }
| |_- first implementation here
14 |
15 | / impl<K, V, V_> Foo<HashMap<K, V_>> for HashMap<K, V>
16 | | where
17 | | K: Hash + Eq,
18 | | V: Into<V_>,
19 | | {
20 | | const FOO: i32 = 2;
21 | | }
| |_^ conflicting implementation for `std::collections::HashMap<_, _>`
据我所知,问题在于这里存在歧义 - 如果两者都是合法的,应该选择哪个实现?理想情况下,我想要以下内容:
As I understand it, the problem is that there is an ambiguity here - which implementation should be picked if both are legal? Ideally I'd like to have the following:
- 上面的代码(或一些变通方法)应该可以正常编译.
- 在调用站点,如果给定类型只有一种
impl
可能,则选择该类型. - 在调用站点,如果有多个
impl
可能,那么就是一个错误(一致性问题).
- The above code (or some work around) should compile fine.
- At the call site, if there is only one
impl
possible for the given type, then that one is picked. - At the call site, if there are multiple
impl
s possible, then it is an error (coherence issues).
更简洁地说,我希望在调用站点而不是定义站点完成歧义解决.有没有可能有这种行为?
More succinctly, I want ambiguity resolution to be done at the call site, rather than at the definition site. Is it possible to have this behavior?
推荐答案
事实上,您可以在这里应用一个技巧.
There is, in fact, a trick you may be able to apply here.
为了让编译器为您选择一个impl
,它必须附加到一个可以推断的类型参数.您可以向 trait Foo
添加类型参数并创建标记结构,以便 impl
不再重叠:
In order for the compiler to pick an impl
for you, it has to be attached to a type parameter that can be inferred. You can add a type parameter to trait Foo
and create marker structs so that the impl
s no longer overlap:
trait Foo<K, U> {
const FOO: i32;
}
struct ByKeyInto;
impl<K, K_, V> Foo<HashMap<K_, V>, ByKeyInto> for HashMap<K, V>
where
K: Hash + Eq + Into<K_>,
{
const FOO: i32 = 1;
}
struct ByValInto;
impl<K, V, V_> Foo<HashMap<K, V_>, ByValInto> for HashMap<K, V>
where
K: Hash + Eq,
V: Into<V_>,
{
const FOO: i32 = 2;
}
由于 Foo<_, ByKeyInto>
和 Foo<_, ByValInto>
是不同的特征,impl
不再重叠.当您使用需要 Foo<_, U>
的泛型函数用于某些 U
时,编译器可以去寻找一种有效的类型,它确实 如果可证明只有一种可能性,则解析为具体类型.
Since Foo<_, ByKeyInto>
and Foo<_, ByValInto>
are different traits, the impl
s no longer overlap. When you use a generic function that requires Foo<_, U>
for some U
, the compiler can go looking for a type that works, and it does resolve to a concrete type if there is provably only one possibility.
这是一个代码示例,它通过为 U 选择
ByKeyInto
或 ByValInto
在每个调用站点编译和推断正确的 impl
代码>:
Here's an example of code that compiles and infers the correct impl
at each call site by picking ByKeyInto
or ByValInto
for U
:
fn call_me<T, U>(_: T)
where
T: Foo<HashMap<String, i32>, U>,
{
println!("{}", T::FOO);
}
fn main() {
let x: HashMap<&str, i32> = HashMap::new();
call_me(x);
let y: HashMap<String, bool> = HashMap::new();
call_me(y);
}
这会打印 (playground
This prints (playground):
1
2
<小时>
然而,由于 Into
是自反的(也就是说,T
为所有 T
实现了 Into
),如果你想对 HashMap
使用 Foo<HashMap<K, V>>
这很尴尬.由于在这种情况下 重叠了 impl
,您必须通过 turbofish (::<>
) 选择一个.
However, since Into
is reflexive (that is, T
implements Into<T>
for all T
), this is awkward if you want to use Foo<HashMap<K, V>>
for HashMap<K, V>
. Since there are overlapping impl
s in this case, you have to choose one by turbofish (::<>
).
let z: HashMap<String, i32> = HashMap::new();
call_me::<_, ByKeyInto>(z); // prints 1
call_me::<_, ByValInto>(z); // prints 2
这篇关于我可以避免使用泛型来解决 trait 实现的急切歧义吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!