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

Epoch.Next Tracking Issue #46889

Closed
nikomatsakis opened this Issue Dec 20, 2017 · 25 comments

Comments

Projects
None yet
@nikomatsakis
Contributor

nikomatsakis commented Dec 20, 2017

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 #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<String>::

#![feature(match_default_bindings)]

fn print_opt_string(x: &Option<String>) {
    // Here, `x` has type `&Option<String>`...
    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 for the full details.

Easier borrowing (a.k.a. NLL)
Feature gates #![feature(nll)]
Primary tracking issue #43234
Other tracking issues #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.

#![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”:

#![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 #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)
  • 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)
    • Lint against “silent elision” in structs (e.g., Foo instead of Foo<'_>) (issue #45992)
🔴 Infer `T: 'a` outlives requirements on type definitions
Feature gates N/A — not yet implemented
Tracking issue #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:

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<Item = u32>) 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:

#![feature(universal_impl_trait, conservative_impl_trait, dyn_trait)]

fn apply(c: impl Fn(u32) -> u32, arg: u32) -> u32 {
  // equivalent to `fn apply<F>(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:

#![feature(universal_impl_trait, conservative_impl_trait, dyn_trait)]

fn even_numbers() -> impl Iterator<Item = u32> {
  (0..).map(|x| x * 2)
}

fn main() {
  for x in even_numbers().take(5) {
    println!("{}", x);
  }
}

Using dyn Trait:

#![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:

Closures implementing `Clone` or `Copy`
Feature gates #![feature(copy_closures, clone_closures)]
Tracking issue #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.)

#![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 #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.)

#![feature(trait_alias)]

trait SendWrite = Write + Send + Sync;

fn foo<T: SendWrite>(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, but the semantics are not yet implemented.

🔴 Generic associated types
Feature gates #![feature(generic_associated_types)]
Tracking issue #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.)

use std::vec;

trait Iterable {
    type Iterator<'a>;
}

impl<T> Iterable for Vec<T> {
    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 #42327

You can now use the ? operator in functions that return Option, not just Result. Furthermore, through the (as yet unstable) Try trait, you can extend ? to operate on types of your own.

Example

/// Returns `None` if either `a` or `b` is `None`,
/// but otherwise returns `a + b`.
fn maybe_add(a: Option<u32>, b: Option<u32>) -> Option<u32> {
    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 #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 #46479, which has not yet landed.

Example

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 #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 #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)

#![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 #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

This comment has been minimized.

nyarly commented Dec 21, 2017

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

@tcr

This comment has been minimized.

tcr commented Dec 21, 2017

Some links for clarify and streamline paths: #45388 #46531

@aturon

This comment has been minimized.

Member

aturon commented Dec 21, 2017

Fixed, thanks!

@glyn

This comment has been minimized.

glyn commented Dec 21, 2017

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

This comment has been minimized.

vext01 commented Dec 21, 2017

Looking forward to those ownership improvements :)

@aturon

This comment has been minimized.

Member

aturon commented Dec 21, 2017

@glyn Fixed, thanks!

@durka

This comment has been minimized.

Contributor

durka commented Dec 21, 2017

Should specialization and macros 2.0 be tracked here?

@jonathandturner

This comment has been minimized.

Contributor

jonathandturner commented Dec 21, 2017

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

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Dec 21, 2017

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

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Dec 21, 2017

@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

This comment has been minimized.

nixpulvis commented Dec 22, 2017

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

This comment has been minimized.

Contributor

istankovic commented Dec 22, 2017

The description of the impl Trait example looks incorrect:

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

@newpavlov

This comment has been minimized.

Contributor

newpavlov commented Dec 25, 2017

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

This comment has been minimized.

Member

retep998 commented Dec 25, 2017

@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

This comment has been minimized.

Contributor

newpavlov commented Dec 25, 2017

@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

This comment has been minimized.

MoSal commented Dec 25, 2017

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

@CAD97

This comment has been minimized.

Contributor

CAD97 commented Jan 4, 2018

#46479 (? in main) has landed

@gamozolabs

This comment has been minimized.

gamozolabs commented Jan 12, 2018

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

This comment has been minimized.

Member

shepmaster commented Jan 13, 2018

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

This comment has been minimized.

Contributor

nikomatsakis commented Jan 17, 2018

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. =)

@jonathandturner

This comment has been minimized.

Contributor

jonathandturner commented Jan 21, 2018

@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

This comment has been minimized.

Contributor

mark-i-m commented Jan 21, 2018

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

@BatmanAoD

This comment has been minimized.

BatmanAoD commented Mar 14, 2018

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

This comment has been minimized.

Contributor

nikomatsakis commented Mar 14, 2018

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

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Oct 10, 2018

Closing this, seems out of date

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment