Можно ли создать обобщенный impl c для признака, который работает со всеми, кроме одного подмножества типов? - PullRequest
1 голос
/ 13 февраля 2020

Я пытаюсь написать метод generic c, который принимает функцию, которая возвращает либо значение Serialize, либо значение Arc<Serialize>. Мое решение состоит в том, чтобы создать черту, чтобы развернуть Arc при необходимости и создать ссылку на базовое значение:

use serde::Serialize;
use std::sync::Arc;

pub trait Unwrapper {
    type Inner: Serialize;

    fn unwrap(&self) -> &Self::Inner;
}

impl<T> Unwrapper for T
where
    T: Serialize,
{
    type Inner = T;
    fn unwrap(&self) -> &Self::Inner {
        self
    }
}

impl<T> Unwrapper for Arc<T>
where
    T: Serialize,
{
    type Inner = T;
    fn unwrap(&self) -> &Self::Inner {
        self
    }
}

fn use_processor<F, O>(processor: F)
where
    O: Unwrapper,
    F: Fn() -> O,
{
    // do something useful processor
}

Я получаю ошибку E0119 из-за потенциальной возможности реализации Arc Serialize в будущем, например, если я включу функцию ящика serde, чтобы разрешить только это:

error[E0119]: conflicting implementations of trait `Unwrapper` for type `std::sync::Arc<_>`:
  --> src/lib.rs:20:1
   |
10 | / impl<T> Unwrapper for T
11 | | where
12 | |     T: Serialize,
13 | | {
...  |
17 | |     }
18 | | }
   | |_- first implementation here
19 | 
20 | / impl<T> Unwrapper for Arc<T>
21 | | where
22 | |     T: Serialize,
23 | | {
...  |
27 | |     }
28 | | }
   | |_^ conflicting implementation for `std::sync::Arc<_>`

Я не хочу делать это, поскольку я хочу только разрешить Arc вверху уровень, а не в пределах значения (по тем же причинам функция не включена по умолчанию). Учитывая это, есть ли способ отключить мой первый impl только для Arc? Или есть лучший подход к проблеме?

1 Ответ

3 голосов
/ 14 февраля 2020

Ваша попытка не работает, потому что невозможно иметь перекрывающиеся реализации признака.

Ниже приведена попытка написать обобщенный метод c, который принимает значение Serialize или Arc Serialize.

Использует черту Borrow и ее общую реализацию для любого T.

Обратите внимание на использование синтаксиса turbo fi sh на вызывающем сайте метод generi c.

use std::sync::Arc;
use std::borrow::Borrow;
use serde::Serialize;

#[derive(Serialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn myserialize<T: Borrow<I>, I: Serialize>(value: T) {
    let value = value.borrow();
    let serialized = serde_json::to_string(value).unwrap();
    println!("serialized = {}", serialized);
}


fn main() {
    let point = Point { x: 1, y: 2 };
    myserialize(point);

    let arc_point = Arc::new(Point { x: 1, y: 2 });
    myserialize::<_, Point>(arc_point);

}
...