Tokio: Unable to set trait bound to tokio::prelude::{AsyncRead,AsyncWrite}

Created on 20 Nov 2019  路  2Comments  路  Source: tokio-rs/tokio

Version

[dependencies]
futures = { version = "0.3.1", features = ["compat"] }
tokio = { git = "https://github.com/tokio-rs/tokio" }

Platform

rustc 1.41.0-nightly (3e525e3f6 2019-11-18)

Linux tschunk 5.3.0-pf8 #2 SMP PREEMPT Sat Nov 16 16:19:31 CET 2019 x86_64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz GenuineIntel GNU/Linux

Description

Hey. Apologies if this is not a bug, but it's been causing me trouble.

I wanted to set a trait bound to AsyncRead + AsyncWrite, where I experienced troubles.
After trying to fix it for a long time, I decided to write a minimal file to test with.

Minimal example:

use tokio::prelude::*;
use std::pin::Pin;
use std::task::{Poll, Context};
use tokio::io as tok_io;

struct Test<T: AsyncRead>(T);

impl<T: AsyncRead> Test<T> {
    fn poll_read(self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll<Result<usize>
        self.0.poll_read(cx, buf)
    }
}

Errors with:

error[E0599]: no method named `poll_read` found for type `T` in the current scope
  --> src/main.rs:10:16
   |
10 |         self.0.poll_read(cx, buf)
   |                ^^^^^^^^^ method not found in `T`
   |
   = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `poll_read`, perhaps you need to restrict type parameter `T` with it:
   |
8  | impl<T: tokio::io::async_read::AsyncRead + AsyncRead> Test<T> {
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Trying to apply rustc's hint, I noticed that tokio::io::async_read::AsyncRead is private, so that's not gonna work for me either.
Any help is appreciated, there is a big chance I'm just doing something wrong.

Most helpful comment

Hey! Thanks for your reply.
While your answer works, I really wanted to get rid of the unsafe.
So, I came up with this:

use tokio::prelude::*;
use std::pin::Pin;
use std::task::{Poll, Context};
use tokio::io as tok_io;
use std::marker::Unpin;

struct Test<T: Unpin + AsyncRead>(T);

impl<T: Unpin + AsyncRead> Test<T> {
    fn poll_read(self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll<Result<usize, tok_io::Error>> {
        AsyncRead::poll_read(Pin::new(&mut self.get_mut().0), cx, buf)
    }
}

While definitly not winning any awards for prettiness, it does work without unsafe. Many thanks!

All 2 comments

Hi! The thing is, you have to make some promises to the compiler. Since Test is pinned, T: AsyncRead in Test is pinned too right? Not necessarily, that has to be done manually.

use tokio::prelude::*;
use std::pin::Pin;
use std::task::{Poll, Context};
use tokio::io as tok_io;

struct Test<T: AsyncRead>(T);

impl<T: AsyncRead> Test<T> {
    fn poll_read(self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
        /// https://doc.rust-lang.org/std/pin/#pinning-is-structural-for-field
        /// I promise that our Test struct remains pinned in memory at least until poll_read below returns.
        let pinned_inner = unsafe { self.map_unchecked_mut(|s| &mut s.0) };
        pinned_inner.poll_read(cx, buf)
    }
}

I think this is because if you would put something like Vec<E> inside of Test, then the E may move because of Vec realloc, even though the Test does not, since it is pinned. We have to promise that that is not going to happen. Please correct me if I'm wrong.

Hey! Thanks for your reply.
While your answer works, I really wanted to get rid of the unsafe.
So, I came up with this:

use tokio::prelude::*;
use std::pin::Pin;
use std::task::{Poll, Context};
use tokio::io as tok_io;
use std::marker::Unpin;

struct Test<T: Unpin + AsyncRead>(T);

impl<T: Unpin + AsyncRead> Test<T> {
    fn poll_read(self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll<Result<usize, tok_io::Error>> {
        AsyncRead::poll_read(Pin::new(&mut self.get_mut().0), cx, buf)
    }
}

While definitly not winning any awards for prettiness, it does work without unsafe. Many thanks!

Was this page helpful?
0 / 5 - 0 ratings