How to accept either ownerhip or mutable reference for the same method

116 Views Asked by At

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.

1

There are 1 best solutions below

0
On

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.