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

More tests 4 #1069

Merged
merged 36 commits into from
Jul 9, 2018
Merged

More tests 4 #1069

merged 36 commits into from
Jul 9, 2018

Conversation

MajorBreakfast
Copy link
Contributor

No description provided.

@@ -30,57 +30,8 @@ macro_rules! if_std {
)*)
}

#[macro_export]
macro_rules! pinned_deref {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this macro. It was never used anywhere

}

#[macro_export]
macro_rules! pinned_field {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this macro because it's an anti pattern. Better alternatives are:

  • Usage of PinMut::map_unchecked
  • unsafe_pinned! macro

)* }
}
#[macro_use]
mod macros;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've put them in a macro module This is cleaner, but ultimately when macro imports are implemented I'd like to:

  • Put the poll macros under futures::task
  • Put the pin macros... not sure where. We need to decide that once macros can be imported via use statements

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: Macro imports via use are implemented. They‘re just feature gated. We could use them.

unsafe {
$crate::core_reexport::mem::PinMut::map_unchecked(
self.reborrow(), |x| &mut x.$f
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this to map_unchecked. It works the same.

Also, here and in the following macros the $crate::core_reexport:: prefixes were partly missing.

@@ -43,6 +53,6 @@ impl<A> Future for Flatten<A>
type Output = <A::Output as Future>::Output;

fn poll(mut self: PinMut<Self>, cx: &mut task::Context) -> Poll<Self::Output> {
unsafe { pinned_field!(self, state) }.poll(cx, |a, ()| a)
self.state().poll(cx, |a, ()| a)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed all occurences of the pinned_field! macro

@@ -0,0 +1,7 @@
// Macros redefined here because macro re-exports are unstable.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the reason why all macros are duplicated

@@ -65,7 +65,7 @@ where
impl<'a, T, U> Future for SendAll<'a, T, U>
where
T: Sink + Unpin + 'a + ?Sized,
U: Stream<Item = Result<T::SinkItem, T::SinkError>> + Unpin + 'a + ?Sized,
U: Stream<Item = T::SinkItem> + Unpin + 'a + ?Sized,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signature was wrong

::std::thread::spawn(|| {
block_on(do_it((1_000, 0)).map(move |x| tx.send(x).unwrap()))
});
assert_eq!(500_500, rx.recv().unwrap());
Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it a bit fancier than it was before. Now it adds the numbers 0 + 1 + ... + 1000 = 500,500

}

#[test]
fn unfold_err1() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streams are no longer error-aware, therefore this test doesn't apply anymore.

@MajorBreakfast
Copy link
Contributor Author

@aturon This can be reviewed


#[must_use = "futures do nothing unless polled"]
#[derive(Debug)]
crate enum TryChain<Fut1, Fut2, Data> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryChain is a new mechanism so that and_then and or_else can share a single implementation.

}
};

*this = TryChain::Empty; // Drop fut1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The eager_drop tests want this: They expect that the old futures is dropped before the closure runs.

Self: Sized,
{
map_ok::new(self, f)
MapOk::new(self, op)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made these new functions methods

f: Option<F>,
pub struct MapErr<Fut, F> {
future: Fut,
op: Option<F>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

op is how the standard library's Result methods call this

/// Converts a `TryFuture` into a normal `Future`
#[derive(Debug)]
#[must_use = "futures do nothing unless polled"]
pub struct IntoFuture<Fut> {
Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new invention. I originally planned to use it for something, but my plan then changed and I did it another way. I suggest to keep it, though. I think it could be useful.

@MajorBreakfast
Copy link
Contributor Author

Added tests for basic combinators and oneshot::channel()

@MajorBreakfast
Copy link
Contributor Author

Woohoo. Green CI 🎉

And I didn't cheat. To the contrary: The docs now actually build and the CI runs the tests again

@@ -23,126 +25,25 @@

#![no_std]

#![deny(missing_docs, missing_debug_implementations, warnings)]
#![deny(missing_docs, missing_debug_implementations)]
Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rustdoc for reexports seems broken. The crates all build fine individually (e.g. cargo doc -p futures-core). But when reexported in the futures crate we get a bunch of warnings

//! [`Receiver`]: struct.Receiver.html
//! [`Stream`]: ../../futures_core/stream/trait.Stream.html
//! [`Receiver::poll_next`]:
//! ../../futures_core/stream/trait.Stream.html#tymethod.poll_next
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added these links like so. I wasn't able to make it build otherwise. Seems to be a bug in all //! comments

pub use futures_core::stream::Stream;
pub use futures_util::stream::StreamExt;
pub use futures_sink::Sink;
pub use futures_util::sink::SinkExt;
Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed these top level exports because using them is an anti-pattern. It's much easier to either import from the right location like futures::task::Poll or to import the prelude futures::prelude::*. Anything in-between doesn't make sense in my opinion, it just makes the layout of the futures crate less clear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. I don't see any anti-pattern. I frequently have use futures::{Future, Stream} in modules. Making them harder to import doesn't improve anything.

Glob imports and preludes, however, definitely are an anti-pattern.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @seanmonstar, having to begin every file with

use futures::{core::{future::Future, stream::Stream}, utils::{future::FutureExt, stream::StreamExt}};

Instead of

use futures::{Future, Stream, FutureExt, StreamExt};

Would be very annoying.

Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nemo157 @seanmonstar Thanks for your comments!

My reaction is that all this top-level stuff accumulates. There is also:

Context, ContextExt, CoreFutureExt, FutureObj, LocalFutureObj, TryFuture, TryFutureExt and TryStream, TryStreamExt, Sink, SinkExt

These are just the most common imports, but there are obviously more that we could add. (FutureObj exists because dyn Trait is currently impossible for traits that use arbitrary self types) My intention behind removing these top-level imports is that it makes the docs better. Top level exports are a bad thing because they destroy the structure. If everything is exported at the top level anyway, there is no real reason to have submodules at all. How can we decide which things are considered important enough to be exported at the top level? How will it grow over time? How would it look like if the standard library did it? I think that it doesn't scale and we shouldn't do it.
The hierarchy I'm proposing here is the same as in libcore: libcore also differentiates between the submodules future and task. And a stream submodule will probably exist in the future.

@Nemo157 FutureExt is defined in the utils crate, but there's no such module. All extensions live in the respective submodules future, stream and sink

This is how I imagine it:

use futures::future::{Future, FutureExt};
use futures::stream::{Stream, StreamExt};
use futures::task::{Context, Poll};

I think that this actually looks pretty nice. That said, the approach I'm proposing here is just a suggestion. This PR has become rather large, so I explictly highlighted this change to make it absolutely obvious. I certainly don't want to decide that for everyone. If you all really dislike it, we can revert to the old approach. I'm confident, though, that the approach of clear separation is better. Now I want to sell it :) If you're on board, then we change it, otherwise we change it back to the old approach. But, take a little time and think about it. I really think this approach is better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think idea of top-level re-exports serves both convenience for common case (easy to reach things you need without clutter) and discoverability. It's hard to argue that Future, Stream and Sink (and their extension traits since 0.2) are core concepts to futures crates. In my view, public submodules still make sense for things that are more specialized, e.g. deeper dive into streaming functionality for writing your own combinators. Being able to import things through futures::{Future, FutureExt, Stream, StreamExt} and futures::stream::{Stream, StreamExt, FilterMap, Empty} is both useful in my opinion if only in different contexts.

Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean:
screen shot 2018-07-09 at 17 32 27

Cluttered:
screen shot 2018-07-09 at 17 40 13

Consider the impression on a new user when he/she opens the docs for the first time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any problem with the traits list there at the bottom. If it were a big deal, they can be doc(hidden) from the main page, but I don't think it's an issue.

I definitely wouldn't make actually using these traits harder just for the docs change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing to note is that futures 0.2 didn't export Context to the top-level. As far as I am aware the standard was to use task::Context everywhere. That doesn't reduce it much though 😦

I wonder if trait aliases could help with all those extension traits. In futures have something like

trait Future = ::core::future::Future + CoreFutureExt + FutureExt;

(although currently the RFC doesn't specify that trait aliases bring methods into scope, I left a comment on the tracking issue to find out).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seanmonstar My main concern is the impression a new user gets when looking at the docs for the first time. I've added a commit that adds the reexports as doc(hidden). As long as there is no big pile of unsorted stuff visible to users, it's fine I guess.

@Nemo157 I think it would be confusing if more than one thing was called a Future. Would impl Future for MyType even work for a trait alias?

pub use futures_util::try_future::{
TryFutureExt,
AndThen, ErrInto, FlattenSink, IntoFuture, MapErr, MapOk, OrElse,
UnwrapOrElse,
Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've put the stuff for TryFuture into the future module (they previously weren't exported)

Copy link
Contributor Author

@MajorBreakfast MajorBreakfast Jul 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the TryFutures do not implement the Future trait. But, this is planned to change as soon as it becomes technically possible. Therefore, putting the extensions for both into a single module makes sense to me. What do you think?

@MajorBreakfast
Copy link
Contributor Author

MajorBreakfast commented Jul 8, 2018

@cramertj In case you're wondering: I moved the futures-util/benches/bilock.rs to futures-util/benches_disabled/bilock.rs in order to disable it. It hasn't been converted to 0.3 yet (e.g. has fn poll_next(&mut self, ... in it)

unsafe_pinned!(future -> A);
unsafe_unpinned!(f -> Option<F>);
/// Creates a new Map.
pub(super) fn new(future: Fut, op: F) -> Map<Fut, F> {
Copy link
Member

@cramertj cramertj Jul 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with changing these new functions to pub(super) methods, but we should keep it consistent-- there are a bunch of other places where this pattern is used (most combinators) and those should be changed as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll definitely do those in another PR. You know that I love consistency 🤓

@cramertj cramertj merged commit 0dce221 into rust-lang:0.3 Jul 9, 2018
@MajorBreakfast
Copy link
Contributor Author

🎉

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

Successfully merging this pull request may close these issues.

5 participants