generics - 我可以避免对使用泛型的特征实现急切的歧义解决吗?
问题描述
考虑以下 Rust 代码 [ 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 编译)。
它无法编译并出现以下错误:
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<_, _>`
据我了解,问题在于这里存在歧义 -如果两者都是合法的,应该选择哪个实现?理想情况下,我想要以下内容:
- 上面的代码(或一些解决方法)应该可以正常编译。
- 在调用站点,如果给定类型只有一种
impl
可能,则选择该类型。 - 在调用现场,如果可能有多个
impl
s,那么它就是一个错误(连贯性问题)。
更简洁地说,我希望在调用站点而不是在定义站点完成歧义解决。有可能有这种行为吗?
解决方案
实际上,您可以在这里应用一个技巧。
为了让编译器为您选择一个impl
,它必须附加到一个可以推断的类型参数。您可以添加类型参数trait Foo
并创建标记结构,以便impl
s 不再重叠:
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
s 不再重叠。当您使用需要Foo<_, U>
some的泛型函数U
时,编译器可以寻找一种有效的类型,并且如果可证明只有一种可能性,它确实会解析为具体类型。
这是一个代码示例,通过选择或forimpl
在每个调用站点编译和推断正确的代码:ByKeyInto
ByValInto
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);
}
这打印(操场):
1
2
然而,由于Into
它是自反的(即T
实现Into<T>
all T
),如果你想使用Foo<HashMap<K, V>>
for ,这很尴尬HashMap<K, V>
。由于在这种情况下存在重叠的impl
s,因此您必须通过 turbofish ( ::<>
) 选择一个。
let z: HashMap<String, i32> = HashMap::new();
call_me::<_, ByKeyInto>(z); // prints 1
call_me::<_, ByValInto>(z); // prints 2
推荐阅读
- python - Python套接字 - 下载文件仅适用于chrome
- google-chrome - chrome 扩展是否会在自动更新时清除其 cookie?
- php - 使用 PHP 参数化查询执行 db2 插入时出错
- python - 如何解决 IndexError:列表索引超出范围?
- vim - 在 Vim 中,如何为代码块进行可视行选择?
- windows - CUDA统一内存和Windows 10
- c# - 如何重置 PipeReader 以便能够在 ASP.NET Core 中重新读取请求正文
- postgresql - 忽略过滤器的 Tableau 计算
- python - “烧瓶实例”没有属性“记录”错误
- c# - 如何使用 EF Core 仅加载第一个相关数据