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

RFC: Stabilize std::prelude #503

Merged
merged 3 commits into from
Dec 20, 2014
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 303 additions & 0 deletions text/0000-prelude-stabilization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
# Summary

Stabilize the `std::prelude` module by removing some of the less commonly used
functionality of it.

# Motivation

The prelude of the standard library is included into all Rust programs by
default, and is consequently quite an important module to consider when
stabilizing the standard library. Some of the primary tasks of the prelude are:

* The prelude is used to represent imports that would otherwise occur in nearly
all Rust modules. The threshold for entering the prelude is consequently quite
high as it is unlikely to be able to change in a backwards compatible fashion
as-is.
* Primitive types such as `str` and `char` are unable to have inherent methods
Copy link

Choose a reason for hiding this comment

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

Are we insisting that this will never change by promising the existence of these traits forever? I was under the impression that we wanted to fix this at some point.

Copy link
Member Author

Choose a reason for hiding this comment

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

If we are able to get inherent methods on primitive types we can revisit this decision, but for now we can't so this is stabilizing on one extension trait per primitive type if necessary.

attached to them. In order to provide methods extension traits must be used.
All of these traits are members of the prelude in order to enable methods on
language-defined types.

This RFC currently focuses on removing functionality from the prelude rather
than adding it. New additions can continue to happen before 1.0 and will be
evaluated on a case-by-case basis. The rationale for removal or inclusion will
be provided below.

# Detailed Design

The current `std::prelude` module was copied into the document of this RFC, and
each reexport should be listed below and categorized. The rationale for
inclusion of each type is included inline.

## Reexports to retain

This section provides the exact prelude that this RFC proposes:

```rust
// Boxes are a ubiquitous type in Rust used for representing an allocation with
// a known fixed address. It is also one of the canonical examples of an owned
// type, appearing in many examples and tests. Due to its common usage, the Box
// type is present.
pub use boxed::Box;

// These two traits are present to provide methods on the `char` primitive type.
// The two traits will be collapsed into one `CharExt` trait in the `std::char`
// module, however instead of reexporting two traits.
pub use char::{Char, UnicodeChar};

// One of the most common operations when working with references in Rust is the
// `clone()` method to promote the reference to an owned value. As one of the
// core concepts in Rust used by virtually all programs, this trait is included
// in the prelude.
pub use clone::Clone;

// It is expected that these traits will be used in generic bounds much more
// frequently than there will be manual implementations. This common usage in
// bounds to provide the fundamental ability to compare two values is the reason
// for the inclusion of these traits in the prelude.
pub use cmp::{PartialEq, PartialOrd, Eq, Ord};

// Iterators are one of the most core primitives in the standard libary which is
// used to interoperate between any sort of sequence of data. Due to the
// widespread use, these traits and extension traits are all present in the
// prelude.
//
// The `Iterator*Ext` traits can be removed if generalized where clauses for
// methods are implemented, and they are currently included to represent the
Copy link

Choose a reason for hiding this comment

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

I don't think we will be able to remove them post-1.0, since adding methods to the Iterator trait is backwards-incompatible.

Copy link
Member Author

Choose a reason for hiding this comment

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

Adding non-default methods is backwards-incompatible, but I believe we're going to consider adding a default method a backwards-compatible change to these extension traits.

Copy link

Choose a reason for hiding this comment

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

Adding a default method is also backwards incompatible since it can clash with methods from other traits or types.

// functionality provided today. The various traits other than `Iterator`, such
// as `DoubleEndedIterator` and `ExactSizeIterator` are provided in order to
// ensure that the methods are available like the `Iterator` methods.
pub use iter::{DoubleEndedIteratorExt, CloneIteratorExt};
pub use iter::{Extend, ExactSizeIterator};
pub use iter::{Iterator, IteratorExt, DoubleEndedIterator};
pub use iter::{IteratorCloneExt};
pub use iter::{IteratorOrdExt};

// As core language concepts and frequently used bounds on generics, these kinds
// are all included in the prelude by default. Note, however, that the exact
// set of kinds in the prelude will be determined by the stabilization of this
// module.
pub use kinds::{Copy, Send, Sized, Sync};

// One of Rust's fundamental principles is ownership, and understanding movement
// of types is key to this. The drop function, while a convenience, represents
// the concept of ownership and relinquishing ownership, so it is included.
pub use mem::drop;

// As described below, very few `ops` traits will continue to remain in the
// prelude. `Drop`, however, stands out from the other operations for many of
// the similar reasons as to the `drop` function.
pub use ops::Drop;

// Similarly to the `cmp` traits, these traits are expected to be bounds on
// generics quite commonly to represent a pending computation that can be
// executed.
pub use ops::{Fn, FnMut, FnOnce};

// The `Option` type is one of Rust's most common and ubiquitous types,
// justifying its inclusion into the prelude along with its two variants.
pub use option::Option::{mod, Some, None};

// In order to provide methods on raw pointers, these two traits are included
// into the prelude. It is expected that these traits will be renamed to
// `PtrExt` and `MutPtrExt`.
pub use ptr::{RawPtr, RawMutPtr};

// This type is included for the same reasons as the `Option` type.
pub use result::Result::{mod, Ok, Err};

// The slice family of traits are all provided in order to export methods on the
// language slice type. The `SlicePrelude` and `SliceAllocPrelude` will be
// collapsed into one `SliceExt` trait by the `std::slice` module. Many of the
// remaining traits require generalized where clauses on methods to be merged
// into the `SliceExt` trait, which may not happen for 1.0.
Copy link

Choose a reason for hiding this comment

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

same comment here as on the Iterator extensions - we may not be able to do this post-1.0 either.

pub use slice::{SlicePrelude, SliceAllocPrelude, CloneSlicePrelude};
pub use slice::{CloneSliceAllocPrelude, OrdSliceAllocPrelude};
pub use slice::{PartialEqSlicePrelude, OrdSlicePrelude};

// These traits, like the above traits, are providing inherent methods on
// slices, but are not candidates for merging into `SliceExt`. Nevertheless
// these common operations are included for the purpose of adding methods on
// language-defined types.
pub use slice::{BoxedSlicePrelude, AsSlice, VectorVector};

// The str family of traits provide inherent methods on the `str` type. The
// `StrPrelude`, `StrAllocating`, and `UnicodeStrPrelude` traits will all be
// collapsed into one `StrExt` trait to be reexported in the prelude. The `Str`
// trait itself will be handled in the stabilization of the `str` module, but
// for now is included for consistency. Similarly, the `StrVector` trait is
// still undergoing stabilization but remains for consistency.
pub use str::{Str, StrPrelude};
pub use str::{StrAllocating, UnicodeStrPrelude};
pub use str::{StrVector};

// As the standard library's default owned string type, `String` is provided in
// the prelude. Many of the same reasons for `Box`'s inclusion apply to `String`
// as well.
pub use string::String;

// Converting types to a `String` is seen as a common-enough operation for
// including this trait in the prelude.
pub use string::ToString;

// Included for the same reasons as `String` and `Box`.
pub use vec::Vec;
```

## Reexports to remove

All of the following reexports are currently present in the prelude and are
proposed for removal by this RFC.

```rust
// While currently present in the prelude, these traits do not need to be in
// scope to use the language syntax associated with each trait. These traits are
// also only rarely used in bounds on generics and are consequently
// predominately used for `impl` blocks. Due to this lack of need to be included
// into all modules in Rust, these traits are all removed from the prelude.
pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not};
pub use ops::{BitAnd, BitOr, BitXor};
pub use ops::{Deref, DerefMut};
pub use ops::{Shl, Shr};
pub use ops::{Index, IndexMut};
pub use ops::{Slice, SliceMut};

// Now that tuple indexing is a feature of the language, these traits are no
// longer necessary and can be deprecated.
pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};

// Interoperating with ascii data is not necessarily a core language operation
// and the ascii module itself is currently undergoing stabilization. The design
// will likely end up with only one trait (as opposed to the many listed here).
// The prelude will be responsible for providing unicode-respecting methods on
// primitives while requiring that ascii-specific manipulation is imported
// manually.
pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr};
pub use ascii::IntoBytes;

// Inclusion of this trait is mostly a relic of old behavior and there is very
// little need for the `into_cow` method to be ubiquitously available. Although
// mostly used in bounds on generics, this trait is not itself as commonly used
// as `FnMut`, for example.
pub use borrow::IntoCow;

// The `c_str` module is currently undergoing stabilization as well, but it's
// unlikely for `to_c_str` to be a common operation in almost all Rust code in
// existence, so this trait, if it survives stabilization, is removed from the
// prelude.
pub use c_str::ToCStr;
Copy link

Choose a reason for hiding this comment

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

should this be commented out?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure I follow, how come this should be commented out?

Copy link

Choose a reason for hiding this comment

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

"this trait is removed from the prelude"

Copy link

Choose a reason for hiding this comment

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

Derp this is the removed section. Nevermind me.


// This trait is `#[experimental]` in the `std::cmp` module and the prelude is
// intended to be a stable subset of Rust. If later marked #[stable] the trait
// may re-enter the prelude but it will be removed until that time.
pub use cmp::Equiv;

// Actual usage of the `Ordering` enumeration and its variants is quite rare in
// Rust code. Implementors of the `Ord` and `PartialOrd` traits will likely be
// required to import these names, but it is not expected that Rust code at
// large will require these names to be in the prelude.
pub use cmp::Ordering::{mod, Less, Equal, Greater};
Copy link
Member

Choose a reason for hiding this comment

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

mod

Is this a typo?

Copy link
Member

Choose a reason for hiding this comment

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

Nope, it imports the Ordering enum, like cmp::{mod, Ordering} would import cmp and cmp::Ordering.

Copy link
Member

Choose a reason for hiding this comment

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

woah awesome, TIL


// With language-defined `..` syntax there is no longer a need for the `range`
// function to remain in the prelude. This RFC does, however, recommend leaving
// this function in the prelude until the `..` syntax is implemented in order to
// provide a smoother deprecation strategy.
pub use iter::range;

// The FromIterator trait does not need to be present in the prelude as it is
// not adding methods to iterators and is mostly only required to be imported by
// implementors, which is not common enough for inclusion.
pub use iter::{FromIterator};

// Like `cmp::Equiv`, these two iterators are `#[experimental]` and are
// consequently removed from the prelude.
pub use iter::{RandomAccessIterator, MutableDoubleEndedIterator};

// I/O stabilization will have its own RFC soon, and part of that RFC involves
// creating a `std::io::prelude` module which will become the home for these
// traits. This RFC proposes leaving these in the current prelude, however,
// until the I/O stabilization is complete.
pub use io::{Buffer, Writer, Reader, Seek, BufferPrelude};

// These two traits are relics of an older `std::num` module which need not be
// included in the prelude any longer. Their methods are not called often, nor
// are they taken as bounds frequently enough to justify inclusion into the
// prelude.
pub use num::{ToPrimitive, FromPrimitive};

// As part of the Path stabilization RFC, these traits and structures will be
// removed from the prelude. Note that the ergonomics of opening a File today
// will decrease in the sense that `Path` must be imported, but eventually
// importing `Path` will not be necessary due to the `AsPath` trait. More
// details can be found in the path stabilization RFC.
pub use path::{GenericPath, Path, PosixPath, WindowsPath};

// This function is included in the prelude as a convenience function for the
// `FromStr::from_str` associated function. Inclusion of this method, however,
// is inconsistent with respect to the lack of inclusion of a `default` method,
// for example. It is also not necessarily seen as `from_str` being common
// enough to justify its inclusion.
pub use str::from_str;

// This trait is currently only implemented for `Vec<Ascii>` which is likely to
// be removed as part of `std::ascii` stabilization, obsoleting the need for the
// trait and its inclusion in the prelude.
pub use string::IntoString;

// The focus of Rust's story about concurrent program has been constantly
// shifting since it was incepted, and the prelude doesn't necessarily always
// keep up. Message passing is only one form of concurrent primitive that Rust
// provides, and inclusion in the prelude can provide the wrong impression that
// it is the *only* concurrent primitive that Rust offers. In order to
// facilitate a more unified front in Rust's concurrency story, these primitives
// will be removed from the prelude (and soon moved to std::sync as well).
//
// Additionally, while spawning a new thread is a common operation in concurrent
// programming, it is nota frequent operation in code in general. For example

Choose a reason for hiding this comment

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

nota => not a

// even highly concurrent applications may end up only calling `spawn` in one or
// two locations which does not necessarily justify its inclusion in the prelude
// for all Rust code in existence.
pub use comm::{sync_channel, channel};
pub use comm::{SyncSender, Sender, Receiver};
pub use task::spawn;
```

# Drawbacks

A fairly large amount of functionality was removed from the prelude in order to
hone in on the driving goals of the prelude, but this unfortunately means that
many imports must be added throughout code currently using these reexports. It
is expected, however, that the most painful removals will have roughtly equal
ergonomic replacements in the future. For example:

* Removal of `Path` and friends will retain the current level of ergonomics with
no imports via the `AsPath` trait.
* Removal of `iter::range` will be replaced via the *more* ergonomic `..`
syntax.

Many other cases which may be initially seen as painful to migrate are intended
to become aligned with other Rust conventions and practices today. For example
getting into the habit of importing implemented traits (such as the `ops`
traits) is consistent with how many implementations will work. Similarly removal
of synchronization primitives allows for consistence in usage of all concurrent
primitives that Rust provides.

# Alternatives

A number of alternatives were discussed above, and this section can otherwise
largely be filled with various permutations of moving reexports between the
"keep" and "remove" sections above.

# Unresolved Questions

This RFC is fairly aggressive about removing functionality from the prelude, but
is unclear how necessary this is. If Rust grows the ability to
backwards-compatibly modify the prelude in some fasion (for example introducing
multiple preludes that can be opted into) then the aggressive removal may not be
necessary.

If user-defined preludes are allowed in some form, it is also unclear about how
this would impact the inclusion of reexports in the standard library's prelude
in some form.