Propose additions to std::prelude

Recently the commit adding TryFrom/TryInto to prelude is reverted as we figured out that this can break some stable code. But as those traits can’t be so useful if they aren’t included in prelude, libs team will explore to do it without serious breakage.

This means it’s a perfect chance to duscuss what more should be added to prelude! Here’s my proposals from my own practice.

1. std itself

std is the single most use case of absolute path syntax like ::std::vec::Iter. And who want to use this name for another thing?

2. Default::default

Default is nice, mostly because they’re #[derive()]able. But Default::default() is sometimes too verbose, and default() is clear enough to inform to construct some default value.

3. std::cmp::{min, max}

These are useful for simple example code to introduce rust language to newcommers, and they have crystal clear names.

Please comment if you like/dislike some proposals above, or have another proposal like them.

3 Likes

Probably not Default::default(). The name default is so generic, it is likely to clash with something else in existing code, which is probably not a Good Thing™. (At best, it would fail to compile; at worst, it would compile and silently do the wrong thing because of the level of lenience Rust provides when it comes to shadowing.)

Related, and one possible way to do the change:

To copy over some things from there (without taking a position either way on them):

  • Debug & Display
  • HashMap & HashSet
  • FromIterator
  • Read & Write
3 Likes

I’d support Debug, Display, HashMap, HashSet, and FromIterator.

It would probably not do either. It wouldn't break this, since local variables just shadow names that are in the prelude:

fn foo() {
    let default = 0;
}

It would only break if something like this happened:

fn default() {
    // ...
}
fn foo() {
    let d = default(); // ERROR: can't tell if you want to run self::default() or Default::default()
}

... but that seems rare enough that breaking it at an epoch change doesn't seem like a problem.

Then simply adding this function to prelude does NOT break anything, right?

#[inline(always)]
pub fn default<T: Default>() -> T {
    Default::default()
}
1 Like

The second example in itself is already bad enough. In addition, default() out of context totally just looks like an arbitrary free function, especially when type inference is involved (which it usually is when using default). It’s very good to have the visual clue of e.g. Default::default() or even an explicit Type::default() when reading default-using code.

1 Like

IMO, Default::default() is completely wasteful stutter. Type::default(), however, should probably be the preferred style in the vast majority of cases.

Given that Type::default() is what you should be writing, it doesn’t make much sense to put Default::default into the prelude. You should specify what type you’re trying to produce the default of.

In my opinion, a lot of things

The traits in particular are the biggest pain point, but common types like Path, HashSet, etc are also annoying.

Also, not having a hashmap/set macro.

Also, the fact that str::from_utf8 (and other “primitive modules”) are not in prelude is a serious ergonomic miss IMO

1 Like

In a situation such as T::AssociatedType::default() I'd rather write just Default::default() if I can.

5 Likes

also, when I write code with a lot of similar but different collection types because I want to experiment with each kind of collection, I’d rather not change each occurrence of an empty collection constructor if I want to switch types. I strongly prefer to just write Default::default() wherever an empty container is needed. A similar argument also applies when you use macros to e.g. mass-implement a trait for several similar but different types.

1 Like

FromIterator

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.