0

I am making a library in Rust around a TCP protocol. And I am also trying to make it async runtime independent.

I would like to make it accept ownership of an existing connection OR accept a mutable reference to it. I.e. I want to let the caller say who should drop the connection.

I have this

pub struct ReadableStream<T: AsyncRead + AsyncWrite> {
    reader: FramedRead<ReadHalf<T>, WordCodec>,
}

pub struct WriteableStream<T: AsyncRead + AsyncWrite> {
    writer: FramedWrite<WriteHalf<T>, WordCodec>,
}

pub struct AsyncStream<T: AsyncRead + AsyncWrite> {
    read_stream: ReadableStream<T>,
    write_stream: WriteableStream<T>,
}

impl<T: AsyncRead + AsyncWrite> From<T> for AsyncStream<T> {
    fn from(stream: T) -> Self {
        let parts = stream.split();
        Self {
            read_stream: ReadableStream::new(parts.0),
            write_stream: WriteableStream::new(parts.1),
        }
    }
}

impl<'stream, T: AsyncRead + AsyncWrite + Unpin> From<&'stream mut T>
    for AsyncStream<&'stream mut T>
{
    fn from(stream: &'stream mut T) -> Self {
        let parts = stream.split();
        Self {
            read_stream: ReadableStream::new(parts.0),
            write_stream: WriteableStream::new(parts.1),
        }
    }
}

But this fails to compile with "conflicting implementation for AsyncStream<&mut _>". If I uncomment the second implementation, the first one compiles. I can also compile only the second one... But I want to have both. I would expect either to be called based on whether from() was called like AsyncStream::from(source) or like AsyncStream::from(&mut source).

So how could I accomplish that?

Note: FramedRead/FramedWrite are from asynchronous-codec. AsyncRead, AsyncWrite, ReadHalf, WriteHalf are from the futures crate. WordCodec is my implementation of the protocol tailored to asynchronous-codec that is irrelevant to the question at hand.

boen_robot
  • 1,450
  • 12
  • 24
  • 1
    Does this answer your question? [How to write a function that accepts a \`Vec\` of borrowed or owned elements?](https://stackoverflow.com/questions/73839107/how-to-write-a-function-that-accepts-a-vec-of-borrowed-or-owned-elements) Just switch out `Borrow` for `BorrowMut` – cafce25 Nov 21 '22 at 12:32
  • It seems that second implementation is subset of the first implementation that why it fails. However I am not sure how you will implement a foreign trait for a foreign type. You might need to wrap things – Nikolay Zakirov Nov 21 '22 at 12:52
  • @NikolayZakirov The AsyncStream struct that the two traits are applied for is the wrapper. – boen_robot Nov 21 '22 at 15:49
  • @cafce25 Thank you for the suggestion, but alas, it does not fully solve it. If I write ```impl> From for AsyncStream { fn from(stream: &mut S)```, I get "conflicting implementation in crate `core`". – boen_robot Nov 21 '22 at 16:02

1 Answers1

0

I decided what I thought is biting the bullet by implementing a method in my struct ( AsyncStream ) to do the conversion, and maybe implement From for the specific runtimes I plan to test against, enabling users to use the struct's method when their runtime is not covered.

This approach works... but furthermore than that, it revealed that what I was trying to do is not exactly feasible for a different reason. I want to have the ability to split the connection into reader and writer, and I can't do that since split() takes ownership.

I'll solve the ownership/mutable reference problem by accepting ownership only, and providing a method that consumes self and returns the wrapped stream. Not exactly the ergonomics I was hoping for, but it addresses the scenario about wanting to let the user use my lib from a point, to a point, and switch from/to something else.

boen_robot
  • 1,450
  • 12
  • 24