rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.74k stars 12.64k forks source link

Epoch.Next Tracking Issue #46889

Closed nikomatsakis closed 6 years ago

nikomatsakis commented 6 years ago

What is this issue?

Have you noticed that there is a lot going on with Rust these days? Over the last year, there’s been a large push to design a number of tweaks to the Rust language, all with the goal of improving ergonomics and productivity for new and experienced users alike. This issue will help you see what changes are in the works and give you tips for trying them out, giving feedback, or helping to get them implemented.

Legend

✅ Available in Nightly in “code complete” form 💛 Available in Nightly but design not fully realized 🔴 Not yet available in Nightly

Ownership

✅ Simplifying how `match` and borrowing interact | Feature gates | `#![feature(match_default_bindings)]` | | -------------- | ---------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/42640 | Matching over borrowed data today in Rust requires a combination of `*` operators, `&` patterns, and `ref` bindings. The `match_default_bindings` feature replaces them with a simpler system; when matching against a value of type `&T`, you can simply ignore the `&` and give a pattern that matches the underlying type `T`. Any bindings in that pattern will be made into references themselves. **Example** [Simple example matching an `&Option`:](https://play.rust-lang.org/?gist=e3e5a50d64a7cfa266036797017431b4&version=stable): ```rust #![feature(match_default_bindings)] fn print_opt_string(x: &Option) { // Here, `x` has type `&Option`... match x { // ...but we give a pattern that matches `Option`, // ignoring the `&`. As a result, the `y` variable // gets the type `&String`. Some(y) => println!("{}", y), None => println!("none"), } // Equivalent to the following in today's Rust: // match x { // &Some(ref y) => ... // &None => ... // } } fn main() { print_opt_string(&Some("foo".to_string())); } ``` **What’s left to be done?** Not much. There are a few corner cases that we might want to fine-tune: see [the tracking issue](https://github.com/rust-lang/rust/issues/42640) for the full details.
✅ Easier borrowing (a.k.a. NLL) | Feature gates | `#![feature(nll)]` | | -------------- | -------------------- | | Primary tracking issue | https://github.com/rust-lang/rust/issues/43234 | | Other tracking issues| rust-lang/rust#44100 | The compiler is now able to understand control flow much more deeply when deciding what paths are borrowed at any particular point in the program. These changes eliminate a large number of confusing borrowing errors, particularly those errors that are due more to imprecision in the analysis. **Examples** [Borrow lifetimes are no longer tied to lexical scopes.](https://play.rust-lang.org/?gist=e71d99214aea46ee102fc5402986094a&version=nightly) ```rust #![feature(nll)] struct Data { value: u32 } impl Data { fn is_odd(&self) -> bool { (self.value & 1) != 0 } } fn main() { let mut vec = vec![Data { value: 1 }]; // In today's Rust, storing `&vec[0]` into a variable // would cause the borrow to last until the end of // the enclosing block. But with `nll` enabled, // the borrow only lasts until the final use of // `first`. let first = &vec[0]; if first.is_odd() { // This means that `vec` can be mutated here. vec.push(Data { value: 22 }); } // Today's Rust, as least if you want to keep // the variable `first`: // let is_odd = { // let first = &vec[0]; // first.is_odd() // }; // if is_odd { .. } } ``` [Invoking `&mut self` methods no longer requires “unnesting”:](https://play.rust-lang.org/?gist=f580b199f5ad4231966af9a1fa1c703d&version=nightly) ```rust #![feature(nll)] struct Counter { data: u32 } impl Counter { fn get(&self) -> u32 { self.data } fn set(&mut self, value: u32) { self.data = value; } } fn main() { let mut c = Counter { data: 0 }; // In today's Rust, this would get a borrow error, // because we first borrow `c` (as the receiver to `set()`) // and then afterwards attempt to evaluate `c.get()`. c.set(c.get() + 1); // Today's Rust: // let tmp = c.get() + 1; // c.set(tmp); } ``` **What’s left to be done?** There is still some engineering effort remaining. For example, some of the error messages are not yet particularly user friendly. In addition, we need to tighten some of the corner cases described in the various RFCs (these are not soundness issues per se, but rather cases where we fear that the analysis may not be forwards compatible with future changes we have in mind). We will be transitioning to the new borrow system, but slowly. We plan to run a “trial period” where we gather feedback and try to find bugs. We also need to do a “warning period” before enabling the new borrowing system by default, as the new system fixes a number of bugs where the compiler incorrectly accepted illegal code (the new implementation is believed to be significantly more robust).
💛 In-band lifetimes | Feature gates | `#![feature(underscore_lifetimes, in_band_lifetimes)]` | | -------------- | ------------------------------------------------------ | | Lints | `#![warn(single_use_lifetime, elided_lifetime_in_path)]` | | Tracking issue | https://github.com/rust-lang/rust/issues/44524 | The current rules that govern explicit lifetime names have some confusing edge cases. These can lead to surprising errors that are hard to diagnose. In addition, the existing annotations can be tedious to supply and get right. The in-band lifetimes RFC aims to adjust the rules to make for a smoother experience overall. **Warning:** while the major features of this RFC are in place, some of the lints are not yet fully implemented. This The highlights: - In functions, you can now use `'_` to indicate an “anonymous lifetime” that you do not care to give a name. This is primarily intended for use with lifetime-parameterized structs; for example, one can now write `Foo<'_>`, whereas before it was common to either write `Foo` (which obscured the fact that a lifetime was present) or to introduce a one-off name like `Foo<'a>` (where `'a` is never used anywhere else). - In fact, lifetime parameters will be required (enforced via lint) on structs and enums in all contexts — in other words, one should not write just `Foo` is `Foo` has a lifetime parameter — but you can use `Foo<'_>` if the specific value is not important. - In functions and impls, you can now leave off explicit lifetime declarations like the `<'a>` in `impl<'a>`. Instead, the intention is that simply annotate the lifetimes that must be the same by giving them explicit names, and use `'_` for lifetimes that are not required to match against other lifetimes. **Examples** TBD **What is left to do?** Some pieces of this design are not yet implemented: - You cannot yet use `'_` or elide lifetimes in impls ([issue #15872](https://github.com/rust-lang/rust/issues/15872)) - Some of the lints that will guide users down the “happy path” are not yet fully implemented: - Lint against single-use lifetimes (instructing users to prefer `'_`) ([issue #44752](https://github.com/rust-lang/rust/issues/44752)) - Lint against “silent elision” in structs (e.g., `Foo` instead of `Foo<'_>`) ([issue #45992](https://github.com/rust-lang/rust/issues/45992))
🔴 Infer `T: 'a` outlives requirements on type definitions | Feature gates | N/A — not yet implemented | | -------------- | ---------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/44493 | Explicit `T: 'x` annotations will no longer be needed on type definitions. We will infer their presence based on the fields of the struct or enum. In short, if the struct contains a reference (directly or indirectly) to `T` with lifetime `'x`, then we will infer that `T: 'x` is a requirement: ```rust struct Foo<'x, T> { // inferred: `T: 'x` field: &'x T } ``` Explicit annotations remain as an option used to control trait object lifetime defaults, and simply for backwards compatibility. **Examples** Coming soon =) **What’s left to be done** Everything

The Trait System

✅ `impl Trait` and `dyn Trait` | Feature gates | `#![feature(universal_impl_trait, conservative_impl_trait, dyn_trait)]` | | -------------- | ----------------------------------------------------------------------- | | Tracking issue | #34511 and #44662 | `impl Trait` is a long awaited feature that allows one to describe types by the traits that they implement. For example, a function like `fn foo(args: impl Iterator)` declares a function `foo` that takes an iterator of `u32` of argument; `impl Trait` can also be used in return position. Currently, `impl Trait` is limited to function signatures and cannot be used in traits or trait impls. In the future, we aim to support the notation in more places. `dyn Trait` is a simple syntactic change: whereas a trait object type used to be written as something like `&Write` (where `Write` is a trait), it is now preferred to write `&dyn Write`, which helps to make clear that (a) `Write` is a trait and (b) that method calls on `Write` will employ dynamic dispatch. Together, these two features help to both grow expressiveness, and to address a common point of user confusion about the role of traits and types. In particular, when using these keywords, a *trait* is never used directly as a type; rather one uses the `impl` or `dyn` keyword to select how to use **Examples** [Using `impl Trait` in argument position:](https://play.rust-lang.org/?gist=22050cdf69421b8e5f91358653feadbd&version=nightly) ```rust #![feature(universal_impl_trait, conservative_impl_trait, dyn_trait)] fn apply(c: impl Fn(u32) -> u32, arg: u32) -> u32 { // equivalent to `fn apply(c: F, arg: u32) where F: Fn(u32) -> u32` c(arg) } fn main() { println!("{}", apply(|x| x * 2, 22)); // prints 44 } ``` [Using `impl Trait` in return position:](https://play.rust-lang.org/?gist=b3711b4eb6d66fbd431aa9ca1a22ac6f&version=nightly) ```rust #![feature(universal_impl_trait, conservative_impl_trait, dyn_trait)] fn even_numbers() -> impl Iterator { (0..).map(|x| x * 2) } fn main() { for x in even_numbers().take(5) { println!("{}", x); } } ``` [Using `dyn Trait`:](https://play.rust-lang.org/?gist=910a5133023c606dc870f1620d9f9e7d&version=nightly) ```rust #![feature(universal_impl_trait, conservative_impl_trait, dyn_trait)] fn apply(c: &dyn Fn(u32) -> u32, arg: u32) -> u32 { c(arg) } fn main() { println!("{}", apply(&|x| x * 2, 22)); // prints 44 } ``` **What’s left to be done?** There are still a few outstanding questions about the syntax, notably the precedence of `impl Trait`. You can get the full details at the tracking issues: - [impl Trait tracking issue](https://github.com/rust-lang/rust/issues/34511) - [dyn Trait tracking issue](https://github.com/rust-lang/rust/issues/44662)
✅ Closures implementing `Clone` or `Copy` | Feature gates | `#![feature(copy_closures, clone_closures)]` | | -------------- | ---------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/44490 | Closures are now copyable / cloneable if the variables that they capture are copyable / cloneable. Note that non-move closures often borrow captured variables instead of taking ownership of them, and hence a closure may be `Copy` even if some of the variable that it uses are not (because it only requires a shared reference to them). **Examples** [(Try it on play.)](https://play.rust-lang.org/?gist=2c617557b88c26cfacba14d8e4167658&version=nightly) ```rust #![feature(copy_closures, clone_closures)] fn main() { let v = vec![1, 2, 3]; // this closure captures `v` by shared reference: let v_len1 = || v.len(); // therefore, it is `Copy`: let v_len2 = v_len1; // and naturally also `Clone`: let v_len3 = v_len1.clone(); assert_eq!(v_len1(), v.len()); assert_eq!(v_len2(), v.len()); assert_eq!(v_len3(), v.len()); } ``` **What’s left to be done?** Gain experience.
🔴 Trait aliases | Feature gates | `#![feature(trait_alias)]` | | -------------- | ---------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/41517 | Trait aliases allow you to make aliases for common used combinations of trait bounds and where-clauses, much like a type alias lets you have an alternate name for a commonly used type. **Example** (Since this feature is not fully implemented yet, example will not actually work.) ```rust #![feature(trait_alias)] trait SendWrite = Write + Send + Sync; fn foo(t: &T) { // ^^^^^^^^^^^^^ equivalent to `T: Write + Send + Sync`. } ``` **What’s left to be done?** Quite a bit. Some of the parsing and infrastructure work [landed in nightly](https://github.com/rust-lang/rust/pull/45047), but the semantics are not yet implemented.
🔴 Generic associated types | Feature gates | `#![feature(generic_associated_types)]` | | -------------- | ---------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/44265 | Generic associated types allow associated types defined in traits to take lifetime or type parameters. This allows for common patterns like an `Iterable` trait which are currently quite difficult to do. **Example** (Since this feature is not fully implemented yet, example will not actually work.) ```rust use std::vec; trait Iterable { type Iterator<'a>; } impl Iterable for Vec { type Iterator<'a> = vec::Iter<'a, T>; } ``` **What’s left to be done** The parsing and various bits of the semantics are implemented, but there is still more to come. See the tracking issue.

Error Handling

✅ `?` applied to `Option` and other types | Feature gates | `#![feature(try_trait)]`, but not needed to use `?` with `Option` | | -------------- | ----------------------------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/42327 | You can now use the `?` operator in functions that return `Option`, not just `Result`. Furthermore, through [the (as yet unstable) `Try` trait](https://doc.rust-lang.org/std/ops/trait.Try.html), you can extend `?` to operate on types of your own. **Example** ```rust /// Returns `None` if either `a` or `b` is `None`, /// but otherwise returns `a + b`. fn maybe_add(a: Option, b: Option) -> Option { Some(a? + b?) } ``` **What’s left to be done?** Gain more experience with the `Try` trait.
✅ `?` in `main()` | Feature gates | `#![feature(termination_trait)]` | | -------------- | ---------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/43301 | You can now give `main()` alternative return types, notably including `Result` types. This enables the use of the `?` operator within `main()`. The goal is to support `?` also in unit tests, but this is not yet implemented. **Note:** Implemented in PR https://github.com/rust-lang/rust/pull/46479, which has not yet landed. **Example** ```rust use std::io::prelude::*; use std::fs::File; fn main() -> io::Result<()> { let mut file = File::create("foo.txt")?; file.write_all(b"Hello, world!")?; } ``` **What’s left to be done?** Once PR https://github.com/rust-lang/rust/pull/46479 lands, you should be able to use examples involving `main`. But the full design also allows for writing unit tests (`#[test]` fns) with `Result` return types as well.

The Module System

✅ Nested groups in imports | Feature gates | `#![feature(use_nested_groups)]` | | -------------- | --------------------------------------------- | | Tracking issue | https://github.com/rust-lang/rust/issues/44494 | You can now nest “import groups”, making it easier to import many names from a single crate without breaking things into multiple `use` statements. **Example** [(Try it on play)](https://play.rust-lang.org/?gist=ac29045631d264a05b60fdf03af67d0a&version=nightly) ```rust #![feature(use_nested_groups)] use std::sync::{ Arc, atomic::{AtomicBool, Ordering}, }; fn main() { let c = Arc::new(AtomicBool::new(true)); let v = c.load(Ordering::SeqCst); println!("v={:?}", v); } ``` **What’s left to be done?** Gain experience, find bugs in the implementation.
💛 Clarify and streamline paths | Feature gates | `#![feature(crate_in_paths, crate_visibility_modifier, non_modrs_mods)]` | -------------- | ---------------------------------------------------------------------------------------------------- | | Lints | `#![warn(unreachable_pub)]` | | Tracking issue | https://github.com/rust-lang/rust/issues/44660 | These changes seek to clarify and streamline Rust's story around paths and visibility for modules and crates. That story will look as follows: - Absolute paths should begin with a crate name, where the keyword `crate` refers to the current crate (other forms are linted, see below) extern crate is no longer necessary, and is linted (see below); dependencies are available at the root unless shadowed. - The `crate` keyword also acts as a visibility modifier, equivalent to today's `pub(crate)`. Consequently, uses of bare `pub` on items that are not actually publicly exported are linted, suggesting `crate` visibility instead. - A `foo.rs` and `foo/` subdirectory may coexist; `mod.rs` is no longer needed when placing submodules in a subdirectory. **Example** **What’s left to be done?**
nyarly commented 6 years ago

the tracking issue link for "Clarify and streamline paths" links to the issue for NLLs.

tcr commented 6 years ago

Some links for clarify and streamline paths: https://github.com/rust-lang/rust/issues/45388 https://github.com/rust-lang/rust/pull/46531

aturon commented 6 years ago

Fixed, thanks!

glyn commented 6 years ago

There is a copy and paste slip in the box at the top of the "impl Trait and dyn Trait" section - the tracking issues should be #34511 and #44662 rather than #42640.

vext01 commented 6 years ago

Looking forward to those ownership improvements :)

aturon commented 6 years ago

@glyn Fixed, thanks!

durka commented 6 years ago

Should specialization and macros 2.0 be tracked here?

sophiajt commented 6 years ago

@nikomatsakis - Not sure if anyone else mentioned it, but NLL says "This feature is blocked on #46862 landing." which has now landed.

nikomatsakis commented 6 years ago

@jonathandturner updated, though nightly still does seem to have the changes.

nikomatsakis commented 6 years ago

@durka

Should specialization and macros 2.0 be tracked here?

I don't think either of those are on track to be part of the next epoch. Specialization no, because it is not on track -- design is still very much in flux. I feel similarly about Macros 2.0 -- for example, there is no RFC or description of the hygiene system.

Note though that not being "part of the epoch" won't necessarily delay features from being available. The epoch is more of an end-point: basically a way to say "hey, a coherent set of features and associated tooling and documentation has landed -- you should give Rust another look!".

nixpulvis commented 6 years ago

Awesome to see so much great work in this!

Where does the work on const items, and constants in types land in respect to the Epoch system?

istankovic commented 6 years ago

The description of the impl Trait example looks incorrect:

declares a function foo that takes an integer of u32 of argument

newpavlov commented 6 years ago

What about const generics? Currently for me it's the most expected feature and I think it will be good for marketing to include it into the new epoch features list.

retep998 commented 6 years ago

@newpavlov New features should only be part of an epoch if they require a change which is not backwards compatible. Otherwise the feature should land normally so that people aren't forced to update their code for the new epoch just to gain access to that feature. Shoving something into an epoch just for the sake of marketing is a bad idea.

newpavlov commented 6 years ago

@retep998 I was under impression that epochs will be also used to highlight the most important additions to the language since the last epoch and looking on the list in the OP most (or even all?) features look backwards compatible to me.

MoSal commented 6 years ago

I wouldn't consider impl Trait green yet. The inability to return impl Trait types in trait methods is a huge limitation.

CAD97 commented 6 years ago

46479 (? in main) has landed

gamozolabs commented 6 years ago

Can we change the legend/keys to be colorblind friendly (different shapes for each key)? I can't tell the difference between the two hearts.

shepmaster commented 6 years ago

I've updated the previous green heart (💚 — "Available in Nightly in “code complete” form") to a checkbox (✅), which appears as a green square for me.

nikomatsakis commented 6 years ago

Can we change the legend/keys to be colorblind friendly (different shapes for each key)? I can't tell the difference between the two hearts.

Sorry about that -- I was actually trying to be colorblind friendly by having at least have the red/green keys have a distinct shape, but I guess I should have further. =)

sophiajt commented 6 years ago

@nikomatsakis - any guidance on what makes the epoch.next list? I know some folks are going to ask about features like const generics and no doubt others.

mark-i-m commented 6 years ago

Const generics seems like a pretty big feature to try to finish this year and get it to stable...

BatmanAoD commented 6 years ago

Should async/await be added to this list, given the recent design breakthroughs (described in the @withoutboats blog posts) and the inclusion in the 2018 Roadmap?

nikomatsakis commented 6 years ago

We have to figure out just how to manage this issue -- I wouldn't take this as an exhaustive list just now.

nikomatsakis commented 6 years ago

Closing this, seems out of date