简体   繁体   中英

Is it possible to create a generic impl for a trait that works with all but one subset of types?

I am trying to write a generic method that accepts a function that returns either a Serialize value or an Arc<Serialize> value. My solution is to create a trait to unwrap the Arc if needed and produce a reference to the underlying value:

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
}

I get a E0119 error due to the potential that Arc may implement Serialize in the future, like if I enable the serde crate's feature to allow just that:

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

I don't want to do this as I only want to allow the Arc at the top level and not within the value (for the same reasons the feature is not on by default). Given this, Is there a way to disable my first impl only for an Arc ? Or is there a better approach to the to problem?

Your attempt does not work because it is not possible to have overlapping implementations of a trait.

Below an attempt to write a generic method that accept a Serialize value or an Arc of a Serialize value.

It leverages the Borrow trait and its blanket implementation for any T.

Note the use of the turbo fish syntax on the calling site of the generic method.

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM