r/rust•
staticassert

How do I make this code more future-y?

https://play.rust-lang.org/?gist=4ede91865b16e1993862a99df91607e2&version=stable

I know I'm doing something wrong here. My client creates its own core so that I can just block when I make requests.

I don't see how to just return futures without exposing implementation details - I'm trying to write a high level, encapsulated client.

I think maybe the answer is to impl Future for my client, and then lift the Core out of it. But I don't want 'and_then' and things like that, I want 'get_file' etc.

This code is, I think, functional. But it feels incorrect.

Posted Apr 8, 2018, 6:38 PM UTC

78
daborossfern•

I've found it useful to write APIs like this in two parts.

It would mean having one "async" API which does not contain the core, and returns futures on every request, then having a "sync" api structure which holds the async API and the core together, and allows making synchronous requests.

In your specific case, new on the "async" api would probably return some kind of Future<AsyncClient>, and the SyncClient would have a blocking new to match it.

Some crates like reqwest have the async part marked as unstable so it can have breaking changes, so that could be an option too. The async API doesn't have to be a public stable one, but if you want people to use it (the async part), it probably should be.

I don't generally think clients exposing Futures should contain their own cores - but it's very inconvenient for synchronous usage if the user always needs to manage a core as well. This is the main reason I'd go with a two-part API.

3
staticassertOP•

OK, that makes sense. I guess what I'm struggling with is.. how would I expose a Future<AsyncClient> ?

I guess I'd do something like:

TcpStream::connect(&addr, &reactor)
        .and_then(move |socket| {
                Connection::handshake(socket, reactor)
                    .map_err(|_| panic!("failed HTTP/2.0 hand 
shake"))
            })
            .and_then(move |conn| {
                let conn = add_origin::Builder::new()
                    .uri(uri)
                    .build(conn)
                    .unwrap();

            Client {
                connection: FileHistory::new(conn),
            }
        })
2
staticassertOP•

oh... it worked

4
daborossfern•

Yeah, that's what I would do- then just return a Box<Future<Item=AsyncClient, Error=...>>.

1
staticassertOP•

In this case I'm using impl Trait because it'll always return the same type.

But yeah, cool. This actually works... thank you.

2