Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FutureObj::from only allows Output = () #1347

Closed
realcr opened this issue Nov 24, 2018 · 7 comments
Closed

FutureObj::from only allows Output = () #1347

realcr opened this issue Nov 24, 2018 · 7 comments

Comments

@realcr
Copy link

realcr commented Nov 24, 2018

Hi, I have seen this piece of code at alloc/boxed.rs (Futures 0.3.0-alpha.9):

#[unstable(feature = "futures_api", issue = "50547")]
impl<'a, F: Future<Output = ()> + Send + 'a> From<Box<F>> for FutureObj<'a, ()> {
    fn from(boxed: Box<F>) -> Self {
        FutureObj::new(boxed)
    }
}

Full listing here: https://doc.rust-lang.org/src/alloc/boxed.rs.html#833-837

I have a future that returns an output that is not (). When I tried to compile:

FutureObj::from(fut.boxed())

I was surprised to find out that () is the only Output supported for FutureObj::from. As a workaround I am using FutureObj::new. Is this behaviour intentional? I apologize ahead of time if I'm asking something trivial here, I'm still new to this codebase.

@hcpl
Copy link
Contributor

hcpl commented Nov 24, 2018

The code you linked is from the standard library. You probably meant this instead?

https://github.com/rust-lang-nursery/futures-rs/blob/4f17d8ebeed356f7f17c2a3e561f2adeba3e5e4e/futures-core/src/future/future_obj.rs#L245-L249

Equivalent link to futures-preview 0.3 API reference docs: https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.9/src/futures_core/future/future_obj.rs.html#243-247

@realcr
Copy link
Author

realcr commented Nov 24, 2018

@hcpl: Yes, you are correct. I meant the link that you have sent.

@Nemo157
Copy link
Member

Nemo157 commented Nov 25, 2018

What do you want to use FutureObj for? As far as I'm aware the only use case is for the Spawn trait (which is limited to Future<Output = ()>) as a workaround until we get unsized r-values, you should be able to just use a Box<T> or Pin<Box<T>> in all other cases.

@realcr
Copy link
Author

realcr commented Nov 25, 2018

@Nemo157 : I have this trait in my code:

pub trait Connector {
    type Address;
    type SendItem;
    type RecvItem;
    fn connect(&mut self, address: Self::Address) 
        -> FutureObj<Option<ConnPair<Self::SendItem, Self::RecvItem>>>;
}

From what I understand you propose to change it to something like:

    fn connect(&mut self, address: Self::Address) 
        -> Pin<Box<Future<Output=Option<ConnPair<Self::SendItem, Self::RecvItem>>>>>;

I remember vaguely that I tried it before and something couldn't compile at some point, but I can't exactly remember what went wrong. I can try it out again and send here the compilation error if I do reach one.

I hope to get you updated this week.

@Nemo157
Copy link
Member

Nemo157 commented Nov 25, 2018

Yeah, that would have needed FutureObj previously. As of about 3 weeks ago there was a change merged to Rust that makes Future now work as an object-safe trait so Pin<Box<dyn Future<Output=Option<ConnPair<Self::SendItem, Self::RecvItem>>>>> should work.

EDIT: see #1348 for more details.

@realcr
Copy link
Author

realcr commented Nov 27, 2018

@Nemo157 : Great respect for your work on this. It's an exciting time to be alive!

I managed to port most of my code to use Pin<Box<dyn Future<...>>> instead of FutureObj. I had two issues though, one I could solve and one I am still working on.

This is what the trait I previously sent looks like now:

pub trait Connector {
    type Address;
    type SendItem;
    type RecvItem;
    fn connect(&mut self, address: Self::Address) 
        -> Pin<Box<dyn Future<Output=Option<ConnPair<Self::SendItem, Self::RecvItem>>> + Send>>;
}

First, I had to add Send inside the trait definition to make things compile, or otherwise I couldn't use it together with ThreadPool.spawn(). I'm not sure if it's a good practice, but it does solve things for now.

The second compilation issue which I couldn't yet solve is that I can't manage to implement the Connector trait due to lifetime issues. Example for one implementation attempt:

impl<A,C,S> Connector for ClientConnector<C,S> 
where
    A: Sync + Send + 'static,
    C: Connector<Address=A, SendItem=Vec<u8>, RecvItem=Vec<u8>> + Sync + Send,
    S: Spawn + Clone + Sync + Send,
{
    type Address = (A, PublicKey);
    type SendItem = Vec<u8>;
    type RecvItem = Vec<u8>;

    fn connect(&mut self, address: (A, PublicKey)) 
        -> Pin<Box<dyn Future<Output=Option<ConnPair<Self::SendItem, Self::RecvItem>>> + Send>> {

        let (relay_address, remote_public_key) = address;
        let relay_connect = self.relay_connect(relay_address, remote_public_key)
            .map(|res| res.ok());
        Box::pinned(relay_connect)
    }
}

This is the compilation error I get:

    |
109 |     fn connect(&mut self, address: (A, PublicKey)) 
    |                - let's call the lifetime of this reference `'1`
...
115 |         Box::pinned(relay_connect)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`

When I was using FutureObj earlier this same code compiled. I might be lacking some fundamental knowledge about the lifetime problem here. Do you have an idea?

I realize that I might be slightly off topic at this point with this issue.
I think that it can be closed.

EDIT:

I managed to get the code compiling by changing the trait method to be:

fn connect<'a>(&'a mut self, address: Self::Address) 
        -> Pin<Box<dyn Future<Output=Option<ConnPair<Self::SendItem, Self::RecvItem>>> + Send + 'a>>;

Thank you for your help!

@realcr realcr closed this as completed Nov 27, 2018
@Nemo157
Copy link
Member

Nemo157 commented Nov 27, 2018

First, I had to add Send inside the trait definition to make things compile, or otherwise I couldn't use it together with ThreadPool.spawn(). I'm not sure if it's a good practice, but it does solve things for now.

That sounds correct, FutureObj implicitly required a sendable Future, you just have to be explicit about it with Pin<Box>.

I managed to get the code compiling by changing the trait method

That's what the old signature basically was, it was able to use lifetime elision with FutureObj to avoid writing it out explicitly. You should also be able to write this as:

fn connect(&mut self, address: Self::Address) 
        -> Pin<Box<dyn Future<Output=Option<ConnPair<Self::SendItem, Self::RecvItem>>> + Send + '_>>;

where '_ is an explicit reference to the elision lifetime (lifetime of the self reference in a method).


This was also briefly discussed on Discord a while ago. I think there will probably be a type alias introduced for this at some point to reduce the extra overhead of using it compared to FutureObj. You could use your own local type alias to simplify it a bit until there's one in std/futures:

type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

fn connect(&mut self, address: Self::Address) 
        -> BoxFuture<'_, Option<ConnPair<Self::SendItem, Self::RecvItem>>>>;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants