Should the Future trait be part of the prelude

Could you mention which? The ones that comes to mind are Read and Write. and it feels like Write is not part of the prelude since there are two Write traits (enabling write!'s "duck typing").


On the one hand, I feel like having Future be part of the prelude currently brings "just" the .poll() method call syntax* (and is thus hardly worth the trouble of adding it to the prelude).

On the other hand, since async fn is sugar for an -> impl Future function, I feel like Future is now a core part of the language, and does thus feel prelude-worthy...

* Having .poll() refer to Future::poll when lacking an inherent impl may disincentivize people from naming other (non-Future-related) methods .poll(). I guess that's good?
2 Likes

Users are expected to use executors, rather than poll() directly, right? So if users use async syntax, this would be only for authors writing "glue" code for futures combinators and executors. Seems niche.

I find myself importing Path and HashMap much much more often, and they aren't in the prelude.

14 Likes

The traits in the ops module are even more so a "core part of the language", most of them being actual lang items, but only Drop and the Fn* traits are in the prelude.

Debug and Display are also important, but not in the prelude.

9 Likes

I recently converted some code over to async/.await and futures-preview 0.3. In my (limited) experience, I needed to call methods on traits like TryFutureExt and TryStreamExt and did not need to call poll() directly anywhere.

I did use Future in a few bounds, but this is included in use futures::prelude::*; which I already needed for the traits above. Since these other traits aren't in std, they can't be included in the standard prelude. Therefore, I don't see a lot of benefit in including Future in the prelude at this time.

6 Likes

I agree that there's other things that are more suited for the prelude (like Path and HashMap) but since I think they belong in the prelude too I voted yes. I don't think external crates should be defining things named Future and I'd like to have a larger prelude in general.

:+1: -- HashMap<K, V> should definitely be there.

5 Likes

It doesn't help that the full path to HashMap is std::collections::HashMap, which is quite long and feels like you're accessing something niche.

Note that types are much easier to add to the prelude than traits, because traits cause method resolution ambiguity but types can be shadowed. So "HashMap should be in the prelude" has very different dynamics from "Future should be in the prelude".

Some related discussion:

7 Likes

Apologies if this is OT, but std::str must be a good candidate for inclusion in the prelude: it's frustrating when you do str::from_utf8 and get an error, despite just using str in some earlier code, and since str is a builtin primitive I would think it unlikely people would want to shadow it (although of course they are still welcome to).

1 Like

Another possibility is to re-export commonly used types & traits at the base std level, so you could do use std::Future, or let mut x = std::HashMap::new(), but again I'm not sure how you would choose what went in it.

Good point about str -- I often wish that the numeric modules would just work too, e.g. u32::MAX.

4 Likes
8 Likes

Seems like crater can figure out whether there is any breakage in general for adding traits to the prelude.

2 Likes

Yes please, how do we make this happen? (Mods: maybe this should be a different topic?)

1 Like

File a PR most likely :slight_smile:

1 Like

Having HashMap in prelude makes it harder to swap out a custom version of HashMap in your codebase. I know that your imports shadow the prelude. But it is easy to end up using the std HashMap without indenting to while adding/modifying code because the compiler wouldn't complain about missing imports.

We should be careful about adding things to the prelude which might have more than one obvious implementation.

2 Likes

@manuthambi Isn't that true for existing things in the prelude as well? E.g. Box, String, and friends.

Well, Box has one cannonical implementation and that other implementations don't make sense (by making Box more than just a owning pointer to some data). With String, yes there could be multiple implementations, but String are used far more than HashMap, so it makes sense String is in prelude.

1 Like

Personally find lack of Error most annoying not to be included. Others suggested Debug/Display/HashMap just don't give the feeling. (Like already mentioned, write! macro also gives annoyance due to Write not included.) Just not been writing enough so far to have feeling either way about Future.

My response to that is that those other traits don't have language-level features built on top. Future has async built on top, which makes it appropriate for it to be in the prelude IMO.

The counter-argument I can think of is that Future isn't imported directly with a use statement all that often I think.