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: Custom preludes #890

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
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
106 changes: 106 additions & 0 deletions text/0000-custom-preludes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
- Feature Name: custom_prelude
- Start Date: 2015-02-19
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary

Add a `#[prelude]` attribute that can be applied to a single module at
crate root to serve as a custom prelude for that crate.

# Motivation

There are a variety of motivations for allowing prelude customization:

* Applications and libraries are often domain-specific, and end up
using the same set of APIs from other libraries over and over
again. Allowing a custom prelude would drastically improve
ergonomics in such cases, setting up an ambient namespace that is
specialized for their domain.

* The lack of prelude customization ends up discouraging certain API
designs that would otherwise be fine, e.g. not using extension
traits, or not modularizing code in order to cut down on import
work.

* Rust has long been moving away from special treatment of built-in
types or of `std`, but today the `std` prelude is still a bit of
magic. That means that non-`std` libraries are at a significant
ergonomic disadvantage -- they always feel a bit "second class".

* Some libraries are already following a "prelude pattern", in which
they export a `prelude` module which is glob-imported by clients --
but these clients must do the import in *each* of their modules,
making it not a true prelude.

# Detailed design

The design is a quite simple extension of the current prelude injector:

* Add a `#[prelude]` attribute that can be applied to a single module
at crate root to serve as a custom prelude for that crate.

* Inject the public contents of the `#[prelude]` the given module as a
glob in all modules defined in the crate (except for the prelude
module itself). This should work via the injector that is already in place.

* If the `#[prelude]` attribute does not appear, and the crate does
not otherwise opt out of the standard prelude, act as if
`#[prelude]` had been applied to `std::prelude::vX` where `X` is the
current prelude version.

By restricting the `#[prelude]` attribute to crate root, this RFC
ensures that it is still straightforward to determine which names are
in scope in a given source location. In particular, this determination
only involves the file defining a module and the file defining the
crate itself.

## Library exmaple

```rust
// Export a prelude for clients, includes only things defined in this crate
pub mod prelude {
pub use my_module::{MyType, MyOtherType};
pub use my_other_module::SomeExtensionTrait;
}

#[prelude]
mod local_prelude {
pub use std::prelude::v1::*;
pub use prelude::*; // use our own prelude
pub use upstream_lib::prelude::* // use preludes from upstream libraries
}
```

## Application example

```rust
#[prelude]
mod prelude {
// std actually provides multiple preludes now
pub use std::prelude::v1::*;
Copy link
Member

Choose a reason for hiding this comment

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

So to use this, I have to figure out what the current "default" prelude is, somehow? I think in the absence of no_std, the default prelude should still be included, with this prelude being added.

pub use std::io::prelude:*;

// use some upstream library preludes
pub use my_favorite_crate::prelude::*;

// ad hoc imports from upstream libs
pub use another_upstream::SomeType;

// local definitions
pub use my_module::MyType;
}
```

# Drawbacks

Adds a bit more complexity around name binding in Rust, in particular
making it mildly less local.

In an extreme case, a very large custom prelude could make it
substantially harder to reason about names in practice, but that
choice is made per-crate and does not affect downstream code.

# Alternatives

None currently proposed.