Могу ли я избежать решительного разрешения неоднозначности для реализации черт с обобщениями? - PullRequest
0 голосов
/ 11 сентября 2018

Рассмотрим следующий код Rust [ детская площадка ]:

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).

Не удается скомпилировать с ошибкой:

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<_, _>`

Насколько я понимаю, проблема в том, что здесь есть неоднозначность - какую реализацию следует выбрать , если оба допустимы? В идеале я хотел бы иметь следующее:

  1. Приведенный выше код (или некоторые обходные пути) должны скомпилироваться нормально.
  2. На сайте вызова, если для данного типа возможна только одна impl, то эта выбирается.
  3. Если на сайте вызова возможно несколько impl s, то это ошибка (проблемы согласованности).

Более кратко, я хочу, чтобы разрешение двусмысленности было сделано на сайте вызовов, а не на сайте определения. Возможно ли такое поведение?

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

На самом деле здесь можно применить один трюк.

Чтобы компилятор выбрал impl для вас, он должен быть прикрепленк параметру типа, который может быть выведен.Вы можете добавить параметр типа в trait Foo и создать структуры маркеров, чтобы impl больше не перекрывались:

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, компилятор может искать тип, который работает, и он разрешает преобразовывать в конкретный тип, если есть только одна возможность.

Вот пример кода, который компилирует и выводит правильные значения impl на каждом сайте вызовов, выбирая 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> для всех T), это неудобноесли вы хотите использовать Foo<HashMap<K, V>> для HashMap<K, V>.Так как перекрываются impl с в этом случае, вы должны выбрать один из них turboish (::<>).

let z: HashMap<String, i32> = HashMap::new();
call_me::<_, ByKeyInto>(z);  // prints 1
call_me::<_, ByValInto>(z);  // prints 2
0 голосов
/ 11 сентября 2018

Могу ли я избежать решительного разрешения неоднозначности для реализации признаков с обобщениями?

номер

Возможно ли иметь [разрешение неоднозначности, которое должно быть сделано на месте вызова, а не на месте определения]?

Количество


Существует (с большой задержкой) RFC для специализации , который позволит перекрывать реализации признаков, но только когда одна из них более специфична, чем другие. Я не верю, что это верно для вашего случая, поэтому это не поможет.

Смотри также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...