Need to fix trait method to accept both TcpStream and OwnedReadHalf

Hi everyone,

in my trait I want to make a method which would work both with tokio::io::BufReader<tokio::net::TcpStream> and tokio::io::BufReader<ttokio::net::tcp::OwnedReadHalf>:

#[async_trait]
pub trait StreamReader {
    async fn read_from(stream: &mut BufReader<TcpStream>) -> Result<Self, FieldError>
        where Self: Sized;
}

Could somebody explain if this is possible or I should implement two methods ?

you need to find (or define) a proper trait bound for you use case, and use a generic type as your parameter type with the trait bound.

in your use case, a good guess would be the AsyncRead trait, something like:

    async fn read_from<Connection>(stream: &mut BufReader<Connection>) -> Result<Self, FieldError>
    where
        Self: Sized,
        Connection: AsyncRead 

alternatively, you may want to hoist the generic type up to the trait (instead of the single method)

1 Like

Seems to me a TcpStream and an OwnedReadHalf both have read() methods because they both implement the AsyncReadExt trait. So playing with that idea I get the following:

use anyhow::Result;
use async_trait::async_trait;
use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;

struct MyThing {}

#[async_trait]
pub trait StreamReader<S>
where
    S: AsyncReadExt + std::marker::Unpin,
{
    async fn read_from(&mut self, mut stream: S) -> Result<()>;
}

impl<S> StreamReader<S> for MyThing
where
    S: AsyncReadExt + std::marker::Unpin,
{
    async fn read_from(&mut self, mut stream: S) -> Result<()> {
        let mut buf = [0; 1024];
        loop {
            match stream.read(&mut buf).await {
                Ok(0) => break,
                Ok(n) => {
                    // Process the received data
                    println!("{:?}", buf);
                }
                Err(e) => {
                    eprintln!("Failed to read from socket: {}", e);
                    break;
                }
            }
        }

        Ok(())
    }
}

Which almost compiles, save a lifetime problem with the self parameter. Which I don't understand of course. Still though it might inspire a solution.

1 Like

yep, and in my case I also should add Send and BufRead:

#[async_trait]
pub trait StreamReader {
    async fn read_from<R>(stream: &mut R) -> Result<Self, FieldError>
        where
            Self: Sized,
            R: AsyncRead + Unpin + AsyncBufRead + Send;
}

Thanks to all for your answers !

I wish I understood my lifetime error:

error[E0195]: lifetime parameters or bounds on method `read_from` do not match the trait declaration
  --> src/server.rs:20:23
   |
13 |     async fn read_from(&mut self, mut stream: S) -> Result<()>;
   |     -----    ----------------------------------- lifetimes in impl do not match this method in trait
   |     |
   |     this bound might be missing in the impl
...
20 |     async fn read_from(&mut self, mut stream: S) -> Result<()> {
   |                       ^ lifetimes do not match method in trait


Looking at it the trait declaration and the impl look the same. Is some weird implicit lifetime thing going on?

I believe you can fix this using async_trait crate:

#[async_trait]
impl<S> StreamReader<S> for MyThing {
    // ...
}

Putting the #[async_trait] on my impl makes it blow up with:
ยดยดยด
error[E0309]: the parameter type S may not live long enough
--> src/server.rs:21:64
|
21 | async fn read_from(&mut self, mut stream: S) -> Result<()> {
| -----____________________________________________^
| | |
| | the parameter type S must be valid for the lifetime 'async_trait as defined here...
22 | | let mut buf = [0; 1024];
23 | | loop {
24 | | match stream.read(&mut buf).await {
... |
37 | | Ok(())
38 | | }
| |
^ ...so that the type S will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
16 | #[async_trait], S: 'async_trait
| +++++++++++++++++

ยดยดยด
I have no idea what it all means. The help suggested there is not even syntactically correct.

I would not use &mut self there at all. But instead I would use StreamReader::read_from(&mut socket) on type which I need (what I actually do in my macros). Of course, StreamReader should be implemented on that type.

I tried that. It did not go well.

I can get it to work by putting the TcpStream or ReaderHalf into the struct:

use anyhow::Result;
use async_trait::async_trait;
use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;

struct MyThing<S>
where
    S: AsyncReadExt + std::marker::Unpin + Send,
{
    stream: S,
}

#[async_trait]
pub trait StreamReader<S>
where
    S: AsyncReadExt + std::marker::Unpin + Send,
{
    async fn read_from(&mut self) -> Result<()>;
}

#[async_trait]
impl<S> StreamReader<S> for MyThing<S>
where
    S: AsyncReadExt + std::marker::Unpin + Send,
{
    async fn read_from(&mut self) -> Result<()> {
        let mut buf = [0; 1024];
        loop {
            match self.stream.read(&mut buf).await {
                Ok(0) => break,
                Ok(n) => {
                    // Process the received data
                    println!("{:?}", buf);
                }
                Err(e) => {
                    eprintln!("Failed to read from socket: {}", e);
                    break;
                }
            }
        }

        Ok(())
    }
}

The use it with the TcpStream:

        let mut mt = MyThing { stream: socket };
        mt.read_from();

Or just the reader half:

        let (mut reader, mut writer) = socket.into_split();
        let mut mt = MyThing { stream: reader };
        mt.read_from();

Interesting. Might be useful for a task I was about to do here next....

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.