From 55d515416d0d5b86a4b3d91a56115a2e38161530 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 19 Jul 2017 01:10:06 -0700 Subject: [PATCH 1/7] First draft of no-more-extern-crate --- text/0000-no-more-extern-crate.md | 103 ++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 text/0000-no-more-extern-crate.md diff --git a/text/0000-no-more-extern-crate.md b/text/0000-no-more-extern-crate.md new file mode 100644 index 00000000000..14c38ee162a --- /dev/null +++ b/text/0000-no-more-extern-crate.md @@ -0,0 +1,103 @@ +- Feature Name: infer-extern-crate +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Infer `extern crate` declarations from the arguments passed to `rustc`. +With this change, projects using Cargo will no longer have to specify +`extern crate`: adding dependencies to `Cargo.toml` will result in the +module being automatically imported. + +# Motivation +[motivation]: #motivation + +One of the principles of Rust is that using external crates should be as +easy and natural as using the standard library. +This allows the standard library to be kept small, and allows mature, standard +solutions to be developed by the community. + +Currently, however, external crates must be specified twice: once in a build +system such as Cargo and again in the source code using `extern crate`. +When external dependencies are conditional (`cfg`) upon feature flags or the +target platform, the conditional logic must appear in both `Cargo.toml` and +the `extern crate` declarations. + +This duplication causes unnecessary effort and results in one more opportunity +for mistakes when working with conditionally-enabled dependencies. + +# Guide-Level Explanation +[guide]: #guide + +When you add a dependency to your `Cargo.toml`, it is immediately usable within +the source of your crate: + +```toml +# Cargo.toml: +name = "my_crate" +version = "0.1.0" +authors = ["Me" ] + +[dependencies] +rand = "0.3" +``` + +```rust +// src/main.rs: + +fn main() { + println!"A random character: {}", rand::random::()); +} +``` + +# Reference-Level Explanation +[reference]: #reference + +External crates are passed to the rust compiler using the `-L` or `-l` flags. +When an external crate is specified this way, an `extern crate name_of_crate;` +declaration will be added to the current crate root. + +However, for backwards-compatibility with legacy `extern crate` syntax, no +automatic import will occur if an `extern crate` declaration for the same +external dependency appears anywhere within the crate. + +Additionally, items such as modules, types, or functions that conflict with +the names of implicitly imported crates will cause the implicit `extern crate` +declaration to be removed. +Note that this is different from the current behavior of the +implicitly-imported `std` module. +Currently, creating a root-level item named `std` results in a name conflict +error. For consistency with other crates, this error will be removed. +Creating a root-level item named `std` will prevent `std` from being included, +and will trigger a warning. + +It will still be necessary to use the `extern crate` syntax when using +`#[macro_use]` to import macros from a crate. This is necessary in order to +prevent macros from being automatically brought into scope and changing the +behavior of existing code. + +# Alternatives +[alternatives]: #alternatives + +- Don't do this. +- Specify external dependencies using only `extern crate`, rather than only +`Cargo.toml`, by using `extern crate foo = "0.2";` or similar. This would +require either `Cargo` or `rustc` to first scan the source before determining +the build dependencies of the existing code, a system which requires fairly +tight coupling between a build system and `rustc`, and which would almost +certainly interact poorly with third-party build systems. + +# Unresolved questions +[unresolved]: #unresolved-questions + +- What interactions does this have with future procedural macros? +- Should we lint/warn on local items shadowing implicitly imported crates? +It seems like a useful warning, but it's also a potential +backwards-compatibility hazard for crates which previously depended on a +crate, didn't import it with `extern crate`, and had a root-level item with +an overlapping name (although this seems like an extreme edge case). +- How can we prevent having to use `extern crate` whenever we need to import +a macro? In a future "macros 2.0" world it may be possible to import macros +using some other syntax, which could resolve this issue. From cef4952baa330856d734b34b955c983f1730a364 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 19 Jul 2017 23:54:34 -0700 Subject: [PATCH 2/7] Fixup macro comments, rustc flag example, and roadmap motivation --- text/0000-no-more-extern-crate.md | 40 ++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/text/0000-no-more-extern-crate.md b/text/0000-no-more-extern-crate.md index 14c38ee162a..c6e26d7e2c4 100644 --- a/text/0000-no-more-extern-crate.md +++ b/text/0000-no-more-extern-crate.md @@ -6,10 +6,14 @@ # Summary [summary]: #summary -Infer `extern crate` declarations from the arguments passed to `rustc`. +This RFC reduces redundant boilerplate when including external crates. +`extern crate` declarations will be inferred from the arguments passed to +`rustc`. With this change, projects using Cargo will no longer have to specify `extern crate`: adding dependencies to `Cargo.toml` will result in the module being automatically imported. +Projects which require more flexibility can still use manual `extern crate` +and will be unaffected by this RFC. # Motivation [motivation]: #motivation @@ -27,6 +31,11 @@ the `extern crate` declarations. This duplication causes unnecessary effort and results in one more opportunity for mistakes when working with conditionally-enabled dependencies. +Allowing the omission of the redundant `extern crate` syntax contributes to the +roadmap goals of +[improving Rust's ergonomics](https://github.com/rust-lang/rust-roadmap/issues/17) +and +[providing easy access to high-quality crates.](https://github.com/rust-lang/rust-roadmap/issues/9) # Guide-Level Explanation [guide]: #guide @@ -55,13 +64,26 @@ fn main() { # Reference-Level Explanation [reference]: #reference -External crates are passed to the rust compiler using the `-L` or `-l` flags. +External crates are passed to the rust compiler using the +`--extern CRATE_NAME=PATH` flag. +For example, `cargo build`-ing a crate `my_crate` with a dependency on `rand` +results in a call to rustc that looks something like +`rustc --crate-name mycrate src/main.rs --extern rand=/path/to/librand.rlib ...`. + When an external crate is specified this way, an `extern crate name_of_crate;` -declaration will be added to the current crate root. +declaration will be added to the current crate root +(note: this behavior won't occur when including a library using the `-l` +or `-L` flags). However, for backwards-compatibility with legacy `extern crate` syntax, no automatic import will occur if an `extern crate` declaration for the same external dependency appears anywhere within the crate. +For example, if `rand = "0.3"` is listed as a dependency in Cargo.toml +and `extern crate rand;` appears somwhere in the crate being compiled, +then no implicit `extern crate rand;` will be added. +If Cargo.toml were to also list another dependency, `log = "0.3"`, and no +`extern crate log;` appears in the crate being compiled, +then an `extern crate log;` would be implicitly added. Additionally, items such as modules, types, or functions that conflict with the names of implicitly imported crates will cause the implicit `extern crate` @@ -77,6 +99,15 @@ It will still be necessary to use the `extern crate` syntax when using `#[macro_use]` to import macros from a crate. This is necessary in order to prevent macros from being automatically brought into scope and changing the behavior of existing code. +However, as specified in +[RFC 1561](https://github.com/rust-lang/rfcs/blob/master/text/1561-macro-naming.md#importing-macros), +macros 2.0 will no longer require `#[macro_use]`, replacing it with +normal `use` declarations, for which no `extern crate` is required. + +One final remaining use case of `extern crate` syntax is for aliasing, i.e. +`extern crate foo as bar;`. There is no way to infer aliasing information from +Cargo.toml, so aliased crates will need to be specied using `extern crate` +syntax. # Alternatives [alternatives]: #alternatives @@ -98,6 +129,3 @@ It seems like a useful warning, but it's also a potential backwards-compatibility hazard for crates which previously depended on a crate, didn't import it with `extern crate`, and had a root-level item with an overlapping name (although this seems like an extreme edge case). -- How can we prevent having to use `extern crate` whenever we need to import -a macro? In a future "macros 2.0" world it may be possible to import macros -using some other syntax, which could resolve this issue. From b5db8c09c6b8fbb96f1a20c04bc2486a5497e498 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 27 Jul 2017 21:56:33 -0700 Subject: [PATCH 3/7] Cleanup --- text/0000-no-more-extern-crate.md | 33 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/text/0000-no-more-extern-crate.md b/text/0000-no-more-extern-crate.md index c6e26d7e2c4..cda8b72ea75 100644 --- a/text/0000-no-more-extern-crate.md +++ b/text/0000-no-more-extern-crate.md @@ -7,11 +7,11 @@ [summary]: #summary This RFC reduces redundant boilerplate when including external crates. -`extern crate` declarations will be inferred from the arguments passed to -`rustc`. -With this change, projects using Cargo will no longer have to specify -`extern crate`: adding dependencies to `Cargo.toml` will result in the -module being automatically imported. +`extern crate` declarations will be inferred from the arguments passed to `rustc`. +With this change, projects using Cargo +(or other build systems using the same mechanism) +will no longer have to specify `extern crate`: +dependencies added to `Cargo.toml` will be automatically imported. Projects which require more flexibility can still use manual `extern crate` and will be unaffected by this RFC. @@ -64,22 +64,27 @@ fn main() { # Reference-Level Explanation [reference]: #reference -External crates are passed to the rust compiler using the +External crates can be passed to the rust compiler using the `--extern CRATE_NAME=PATH` flag. For example, `cargo build`-ing a crate `my_crate` with a dependency on `rand` results in a call to rustc that looks something like `rustc --crate-name mycrate src/main.rs --extern rand=/path/to/librand.rlib ...`. -When an external crate is specified this way, an `extern crate name_of_crate;` -declaration will be added to the current crate root -(note: this behavior won't occur when including a library using the `-l` -or `-L` flags). - -However, for backwards-compatibility with legacy `extern crate` syntax, no -automatic import will occur if an `extern crate` declaration for the same +When an external crate is specified this way, +the crate will automatically brought into scope as if an +`extern crate name_of_crate;` +declaration had been added to the current crate root. +This behavior won't occur when including a library using the `-l` +or `-L` flags. + +We will continue to support the current `extern crate` syntax, +both for backwards compatibility and to enable users who want to use manual +`extern crate` in order to have more fine grained control-- say, if they wanted +to import an external crate only inside an inner module. +No automatic import will occur if an `extern crate` declaration for the same external dependency appears anywhere within the crate. For example, if `rand = "0.3"` is listed as a dependency in Cargo.toml -and `extern crate rand;` appears somwhere in the crate being compiled, +and `extern crate rand;` appears somewhere in the crate being compiled, then no implicit `extern crate rand;` will be added. If Cargo.toml were to also list another dependency, `log = "0.3"`, and no `extern crate log;` appears in the crate being compiled, From 78c1109ca95d1891b98185e882d54a4c1034a168 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 27 Jul 2017 22:07:09 -0700 Subject: [PATCH 4/7] Remove toml annotation to please Github --- text/0000-no-more-extern-crate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-no-more-extern-crate.md b/text/0000-no-more-extern-crate.md index cda8b72ea75..4188a068f6c 100644 --- a/text/0000-no-more-extern-crate.md +++ b/text/0000-no-more-extern-crate.md @@ -43,7 +43,7 @@ and When you add a dependency to your `Cargo.toml`, it is immediately usable within the source of your crate: -```toml +``` # Cargo.toml: name = "my_crate" version = "0.1.0" From 28188dca2c7634f4fa8c076d793f7df09409d7f8 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Sun, 30 Jul 2017 23:48:43 -0700 Subject: [PATCH 5/7] Update extern crate RFC Change to require use of crates. Add mechanism for aliasing crates through Cargo.toml. Address comments from RFC about legacy support and crate linkage. --- text/0000-no-more-extern-crate.md | 81 +++++++++++++++++++------------ 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/text/0000-no-more-extern-crate.md b/text/0000-no-more-extern-crate.md index 4188a068f6c..b536a74d6fd 100644 --- a/text/0000-no-more-extern-crate.md +++ b/text/0000-no-more-extern-crate.md @@ -1,4 +1,4 @@ -- Feature Name: infer-extern-crate +- Feature Name: immediately-usable-extern-crates - Start Date: (fill me in with today's date, YYYY-MM-DD) - RFC PR: (leave this empty) - Rust Issue: (leave this empty) @@ -7,13 +7,12 @@ [summary]: #summary This RFC reduces redundant boilerplate when including external crates. -`extern crate` declarations will be inferred from the arguments passed to `rustc`. With this change, projects using Cargo (or other build systems using the same mechanism) will no longer have to specify `extern crate`: -dependencies added to `Cargo.toml` will be automatically imported. -Projects which require more flexibility can still use manual `extern crate` -and will be unaffected by this RFC. +dependencies added to `Cargo.toml` will be automatically `use`able. +We continue to support `extern crate` for backwards compatibility +with the option of phasing it out in future Rust epochs. # Motivation [motivation]: #motivation @@ -41,7 +40,8 @@ and [guide]: #guide When you add a dependency to your `Cargo.toml`, it is immediately usable within -the source of your crate: +the source of your crate. For example, imagine that you needed to print a random +character. You'd start by adding the `rand` crate to your `Cargo.toml`: ``` # Cargo.toml: @@ -53,11 +53,26 @@ authors = ["Me" ] rand = "0.3" ``` +And then you can immediately `use` the crate: + ```rust // src/main.rs: +use rand; + +fn main() { + let c: char = rand::random(); + println!("A random character: {}", c); +} +``` + +Alternatively, we can `use` just the specific function we need: + +```rust +use rand::random; fn main() { - println!"A random character: {}", rand::random::()); + let c: char = random(); + println!("A random character: {}", c); } ``` @@ -70,29 +85,21 @@ For example, `cargo build`-ing a crate `my_crate` with a dependency on `rand` results in a call to rustc that looks something like `rustc --crate-name mycrate src/main.rs --extern rand=/path/to/librand.rlib ...`. -When an external crate is specified this way, -the crate will automatically brought into scope as if an -`extern crate name_of_crate;` -declaration had been added to the current crate root. -This behavior won't occur when including a library using the `-l` +When an external crate is specified this way, it will be automatically +available to any module in the current crate through `use` statements or +absolute paths (e.g. `::rand::random()`). It will _not_ be automatically +imported at root level as happens with current `extern crate`. +None of this behavior will occur when including a library using the `-l` or `-L` flags. -We will continue to support the current `extern crate` syntax, -both for backwards compatibility and to enable users who want to use manual -`extern crate` in order to have more fine grained control-- say, if they wanted -to import an external crate only inside an inner module. -No automatic import will occur if an `extern crate` declaration for the same -external dependency appears anywhere within the crate. -For example, if `rand = "0.3"` is listed as a dependency in Cargo.toml -and `extern crate rand;` appears somewhere in the crate being compiled, -then no implicit `extern crate rand;` will be added. -If Cargo.toml were to also list another dependency, `log = "0.3"`, and no -`extern crate log;` appears in the crate being compiled, -then an `extern crate log;` would be implicitly added. +We will continue to support the current `extern crate` syntax for backwards +compatibility. `extern crate foo;` will behave just like it does currently. +Writing `extern crate foo;` will not affect the availability of `foo` in +`use` and absolute paths as specified by this RFC. Additionally, items such as modules, types, or functions that conflict with -the names of implicitly imported crates will cause the implicit `extern crate` -declaration to be removed. +the names of implicitly imported crates will result in a warning and will +require the external crate to be brought in manually using `extern crate`. Note that this is different from the current behavior of the implicitly-imported `std` module. Currently, creating a root-level item named `std` results in a name conflict @@ -109,10 +116,20 @@ However, as specified in macros 2.0 will no longer require `#[macro_use]`, replacing it with normal `use` declarations, for which no `extern crate` is required. -One final remaining use case of `extern crate` syntax is for aliasing, i.e. -`extern crate foo as bar;`. There is no way to infer aliasing information from -Cargo.toml, so aliased crates will need to be specied using `extern crate` -syntax. +One remaining use case of `extern crate` syntax is for aliasing, i.e. +`extern crate foo as bar;`. In order to support aliasing, a new "alias" key +will be added to the `Cargo.toml` format. +Users who want to use the `rand` crate but call it `random` instead can now +write `rand = { version = "0.3", alias = "random" }`. + +When compiling, an external crate is only included if it is used +(through either `extern crate`, `use`, or absolute paths). +This prevents unnecessary inclusion of crates when compiling crates with +both `lib` and `bin` targets, or which bring in a large number of possible +dependencies (such as +[the current Rust Playground](https://users.rust-lang.org/t/the-official-rust-playground-now-has-the-top-100-crates-available/11817)). +It also prevents `no_std` crates from accidentally including `std`-using +crates. # Alternatives [alternatives]: #alternatives @@ -134,3 +151,7 @@ It seems like a useful warning, but it's also a potential backwards-compatibility hazard for crates which previously depended on a crate, didn't import it with `extern crate`, and had a root-level item with an overlapping name (although this seems like an extreme edge case). +- `extern crate foo` has linking side effects even if `foo` isn't visibly +used from Rust source. After this change, `use foo;` would have similar +effects. This seems potentially undesirable-- what's the right way of handling +crates which are brough in only for their side effects? From 80839b5982a55ca8774eb17ee81bb5dd364e3eb9 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 31 Jul 2017 10:11:36 -0700 Subject: [PATCH 6/7] Fix typo --- text/0000-no-more-extern-crate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-no-more-extern-crate.md b/text/0000-no-more-extern-crate.md index b536a74d6fd..b6b798f4626 100644 --- a/text/0000-no-more-extern-crate.md +++ b/text/0000-no-more-extern-crate.md @@ -154,4 +154,4 @@ an overlapping name (although this seems like an extreme edge case). - `extern crate foo` has linking side effects even if `foo` isn't visibly used from Rust source. After this change, `use foo;` would have similar effects. This seems potentially undesirable-- what's the right way of handling -crates which are brough in only for their side effects? +crates which are brought in only for their side effects? From 7ac167c214c63ea3c94dbb2a775a3af9ceb2093d Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 31 Jul 2017 11:22:07 -0700 Subject: [PATCH 7/7] Clarify linking of unused dependencies --- text/0000-no-more-extern-crate.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/text/0000-no-more-extern-crate.md b/text/0000-no-more-extern-crate.md index b6b798f4626..f1ec10cea12 100644 --- a/text/0000-no-more-extern-crate.md +++ b/text/0000-no-more-extern-crate.md @@ -122,14 +122,23 @@ will be added to the `Cargo.toml` format. Users who want to use the `rand` crate but call it `random` instead can now write `rand = { version = "0.3", alias = "random" }`. -When compiling, an external crate is only included if it is used +When compiling, an external crate is only linked if it is used (through either `extern crate`, `use`, or absolute paths). -This prevents unnecessary inclusion of crates when compiling crates with -both `lib` and `bin` targets, or which bring in a large number of possible -dependencies (such as -[the current Rust Playground](https://users.rust-lang.org/t/the-official-rust-playground-now-has-the-top-100-crates-available/11817)). -It also prevents `no_std` crates from accidentally including `std`-using -crates. +This prevents unused crates from being linked, which is helpful in a number of +scenarios: +- Some crates have both `lib` and `bin` targets and want to avoid linking both +`bin` and `lib` dependencies. +- `no_std` crates need a way to avoid accidentally linking `std`-using crates. +- Other crates have a large number of possible dependencies (such as +[the current Rust Playground](https://users.rust-lang.org/t/the-official-rust-playground-now-has-the-top-100-crates-available/11817)), +and want to avoid linking all of them. + +In order to prevent linking of unused crates, +after macro expansion has occurred, the compiler will resolve +`use`, `extern crate`, and absolute paths looking for a reference to external +crates or items within them. Crates which are unreferenced in these paths +will not be linked. + # Alternatives [alternatives]: #alternatives