asynchronousrusttraits

Satisfy trait C via trait A *or* B


You can easily demand that a new interface satisfies both A and B:

pub trait Writer: AsyncWriter + BlockingWriter {}

However, I should be able to satisfy the Writer interface by implementing either of them. Having to use an enum for this is painful because then I have to carry the type information for the option that I'm not going to use:

pub enum Writer<A: AsyncWriter, B: BlockingWriter> {
    Async(A),
    Blocking(B),
}

If I want to construct a writer that only uses the async version I still have to pass the type information about the blocking writer. You can be clever and erase the type information from the enum by using dyn:

pub enum Writer<'async, 'blocking> {
    Async(&'async dyn AsyncWriter),
    Blocking(&'blocking dyn BlockingWriter),
}

The problem is this only works for object-safe traits, so it is not entirely fool-proof! Same thing goes for using Box<dyn T> which would also not work in embedded environments because sometimes you don't have a heap.

Is there a better way to think about this problem? Is using separate types the only way?


Solution

  • An "or" constraint wouldn't be very useful (at least not as Rust currently exists) since you wouldn't be able to rely on either when writing code that uses that constraint and there's no syntax for "conditional constraint satisfaction" (maybe with specialization in the future).

    Reading between the lines, it looks like you ultimately want Writer to work for all AsyncWriters and all BlockingWriters. The naive approach to do that directly would be:

    trait Writer {}
    
    impl<T> Writer for T where T: AsyncWriter {}
    impl<T> Writer for T where T: BlockingWriter {}
    

    But that won't work since the compiler will see it as a conflict in the posibility that something is both AsyncWriter and BlockingWriter.

    I would suggest new-type structs that express exactly the kind of writer you are expecting:

    trait Writer {}
    
    struct Async<T>(T);
    impl<T> Writer for Async<T> where T: AsyncWriter {}
    
    struct Blocking<T>(T);
    impl<T> Writer for Blocking<T> where T: BlockingWriter {}
    

    You would have to wrap the sites using AsyncWriter or BlockingWriter as Writer with these wrappers, but it makes it more clear what you're trying to express and how it will do the writing.