nrxus / faux

Struct mocking library for Rust
https://nrxus.github.io/faux/
MIT License
411 stars 14 forks source link

Prototype for pinned self type #30

Closed Wesmania closed 3 years ago

Wesmania commented 3 years ago

Fixes #29.

Signed-off-by: Igor Kotrasinski i.kotrasinsk@gmail.com

nrxus commented 3 years ago

The test included in this PR doesn't have any "tests". The moment I added a test I got compiler errors when running cargo test. I can try to fix them but I need more context on what is the use case. I know the method you are trying to use is Pin<&mut Self> but I also need to know where the Pin came from, was it a Pin<Box>, Pin<Rc>, other...?. Are there other kind of receivers that you would need apart from Pin<&mut Self>

Wesmania commented 3 years ago

I have no idea where the Pin is coming from, I'll check tokio's code and try writing some actual tests. I think prototype was a bad word choice for this PR, it's more of a work-in-progress/sketch.

nrxus commented 3 years ago

Do you have a quick snippet of the code you are working with?

Wesmania commented 3 years ago

Minimal example would be something like this:

use tokio::{io::BufReader, net::TcpStream, io::AsyncRead};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};

pub struct Connection
{
    reader: BufReader<OwnedReadHalf>,
    writer: OwnedWriteHalf,
}

impl Connection {
    pub fn new(stream: TcpStream) -> Self {
        let (r, writer) = stream.into_split();
        let reader = BufReader::new(r);
        Self {reader, writer}
    }
}

impl AsyncRead for Connection {
    fn poll_read(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>,
    ) -> std::task::Poll<std::io::Result<()>> {
        let me = unsafe { self.get_unchecked_mut() };
        let reader = unsafe {std::pin::Pin::new_unchecked(&mut me.reader) };
        AsyncRead::poll_read(reader, cx, buf)
    }
}
nrxus commented 3 years ago

Hmm I see. So poll_read is called somewhere by tokio, it is not something you call directly? Where do you pass your Connection to tokio for it to use?

Wesmania commented 3 years ago

I don't pass it anywhere right now, since until now I just called methods that used inner streams. Simplest use would be calling connection.read using AsyncReadExt trait.

Wesmania commented 3 years ago

Like so:

use tokio::{io::BufReader, net::TcpStream, io::AsyncRead, net::TcpListener, io::AsyncReadExt};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};

pub async fn connections() -> () {
    let listener = TcpListener::bind("localhost").await.unwrap();
    loop {
        match listener.accept().await {
            Err(_) => (),
            Ok((socket, _addr)) => {
                let mut foo = Connection::new(socket);
                let mut num = [0; 10];
                foo.read(&mut num);
            }
        }
    }
}
Wesmania commented 3 years ago

The important bits for Tokio seems to be in src/io/util/read.rs.

Wesmania commented 3 years ago

I put a minimal example here.

nrxus commented 3 years ago

That was extremely helpful, thanks!

This is a bit trickier than the existing "self types" in faux because Pin is never just Pin<T> in the way that Rc<T> is. Pin always wraps some "pointer" type (i.e., Pin<&mut T>). I am hopeful to have a working implementation for this soon that I will put as a commit on top of your branch and merge it as as soon as I get some time. Arbitrary self types is the most complex part of faux, or at least the part where I wrote the messiest code so it's taking me a bit to unravel it 😁

Thanks a lot @Wesmania!

Wesmania commented 3 years ago

Happy to be helpful :) I had a hunch that Pin tends to wrap multiple things, that bit I wrote just happened to work in this case I guess.

nrxus commented 3 years ago

I finally got it to the point where all tests pass again. This should not work for pinned &Self, &mut Self, Rc<Self>, Arc<Self>, and Box<Self. I will make a release after I update the docs.