Skip to content

Syntax for precise capturing: impl Trait + use<..> #125836

Closed

Description

@traviscross

For precise capturing (#123432), we need to decide which syntax to adopt.

The original two, left as an open question in the RFC, were:

  1. impl use<..> Trait
    • This syntax is used throughout the RFC.
  2. use<..> impl Trait
    • This syntax is the worthy challenger.

(See the alternatives section in the RFC for a detailed comparative analysis of these options. In particular, so as to reduce duplication, please read that section carefully before commenting here.)

However, in the design meeting on 2024-06-05, as described below, we settled on placing use<..> within the list of bounds, e.g.:

fn foo<'a>() -> impl Sized + use<'a> {}

This issue is to track the resolution of the open question on syntax left in the RFC.

Tracking:

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jun 1, 2024
  2. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jun 1, 2024
  3. traviscross commented on Jun 6, 2024

    @traviscross
    ContributorAuthor

    We discussed this in the lang design meeting today:

    The conclusion of the team is that we'll make use<..> a bound. That is, we'll support impl use<..> + Trait, impl Trait + use<..>, etc.

    For now, we will support at most one such bound in a list of bounds, and semantically we'll only support these bounds in the item bounds of RPIT-like impl Trait opaque types (i.e., in the places discussed in the RFC).

    We were interested in later extending this so that it could be used in the bounds on associated types and with dyn. This syntactic choice is the most scalable in that way.

    For RPITIT (i.e. within trait definitions), we defer to implementation work and to stabilization whether to support it or to leave this work to later, e.g. to the point at which we support use<..> in the bounds on associated types.

    Note, if we were later to support multiple use<..> items in a list of bounds, that use<T> + use<U> would represent a union, and so use<T> + use<U> would not be equivalent to use<T, U>.

    If the bounds of the opaque type reference generics that are not present in the use<..>, that's an error of course, and a machine-applicable fix seems possible and desirable here.

    We found the idea of this being a bound surprisingly appealing, and people liked that this choice allows for putting + use<..> at the end of a list of bounds. Doing it this way also means we don't need to migrate the ty macro matcher fragment specifier.

    Thanks to @joshtriplett for proposing this appealing option. We resolved a difficult choice between two good options by finding an even better one. That's Rust at its best.

  4. added a commit that references this issue on Jun 18, 2024
  5. changed the title [-]Decide for precise capturing: `impl use<..> Trait` vs `use<..> impl Trait`[/-] [+]Syntax for precise capturing: `impl Trait + use<..>`[/+] on Jun 19, 2024
  6. tmandry commented on Jun 19, 2024

    @tmandry
    Member

    Reopening to formally propose FCP on the above change. I think it is a significant enough delta from the RFC that we would be better off doing an FCP and documenting the rationale now, instead of at stabilization time.

    This FCP resolves the unresolved question in the RFC about syntax.

    @rfcbot fcp merge

  7. rfcbot commented on Jun 19, 2024

    @rfcbot

    Team member @tmandry has proposed to merge this. The next step is review by the rest of the tagged team members:

    No concerns currently listed.

    Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

    cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
    See this document for info about what commands tagged team members can give me.

  8. added
    proposed-final-comment-periodProposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off.
    disposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.
    on Jun 19, 2024
  9. pnkfelix commented on Jun 19, 2024

    @pnkfelix
    Contributor

    I don't know what our official policy is, but @traviscross , would you mind updating the description to reflect the new proposal from #125836 (comment) ?

    (Or I guess even just making the line of the description be a link to that comment...)

  10. 8 remaining items

  11. tmandry commented on Jun 20, 2024

    @tmandry
    Member

    However, in Edition 2021 and lower, the default is to capture only some things. How does use<> work with this? There's a few options:

    • use<> overrides the default capture list. Therefore it can either remove or add variables to the capture list. Then, it's no longer true that use<> always restricts the hidden type, so it doesn't make sense to add a bound.
    • use<> only further restricts the default capture list. I find this a bit unfortunate because it means you'll still need the + Captures<'a> hack to add a lifetime capture.
    • use<> is entirely disallowed in Edition 2021 and lower. This is also unfortunate, and IIUC the aim is to add new features to all editions if possible, and it feels like in this case it should be.

    Neither is particularly great

    I think this is possibly the strongest argument against use<> as a bound, but I'm personally okay with saying that use<> overrides the default capture list in all editions, therefore it makes slightly less sense to conceptualize it as a bound in earlier editions. This is based on the ideas of joint behavior across editions and that editions are meant to be adopted, so while we prefer to have language features work the same across all editions where possible, we're also okay with only delivering the best possible experience in later editions.

  12. marziply commented on Jun 29, 2024

    @marziply

    For unions of use<>, as per the example given: use<T> + use<U>, could we not take inspiration from other languages by using the pipe operator? I feel use<T | U> makes more sense.

  13. added and removed
    final-comment-periodIn the final comment period and will be merged soon unless new substantive objections are raised.
    on Jun 29, 2024
  14. rfcbot commented on Jun 29, 2024

    @rfcbot

    The final comment period, with a disposition to merge, as per the review above, is now complete.

    As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

    This will be merged soon.

  15. added 2 commits that reference this issue on Aug 20, 2024
  16. QuineDot commented on Aug 26, 2024

    @QuineDot

    A flavor of bound that loosens restrictions and cannot be mentioned more than once is not unprecedented. ?Trait bounds do exactly that.

    // Compiles (no warning)
    fn example0<'a>() -> &'a (impl Debug + ?Sized) {
        ""
    }
    
    // Compiles (warning: relaxing a default bound only does something for `?Sized`)
    fn example1<'a>() -> &'a (impl Debug + ?Display) {
        &0
    }
    
    // Fails: error[E0203]: type parameter has more than one relaxed default bound,
    // only one is supported
    fn example2<'a>() -> &'a (impl Debug + ?Display + ?Sized) {
        ""
    }

    Playground.

  17. bsodmike commented on Sep 8, 2024

    @bsodmike

    I'm not sure if anyone has raised this, but maybe this error reporting could be improved to also say "consider adding + use<> rather than just complaining about an immutable borrow?

    error[E0502]: cannot borrow `data` as mutable because it is also borrowed as immutable
     --> src/main.rs:6:5
      |
    5 |     let mut i = indices(&data);
      |                         ----- immutable borrow occurs here
    6 |     data.push(4);
      |     ^^^^^^^^^^^^ mutable borrow occurs here
    7 |     i.next();
      |     - immutable borrow later used here

    Instead,

    error[E0502]: cannot borrow `data` as mutable because it is also borrowed as immutable
     --> src/main.rs:6:5
      |
    5 |     let mut i = indices(&data);
      |                         ----- immutable borrow occurs here
    6 |     data.push(4);
      |     ^^^^^^^^^^^^ mutable borrow occurs here
    7 |     i.next();
      |     - immutable borrow later used here
    
    10 | fn indices<T>(
    11 |     slice: &[T],
    12 | ) -> impl Iterator<Item = usize> + use<> {
      |                                     ----- `impl Trait` must mention all type parameters in scope in `use<...>`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    F-precise_capturing`#![feature(precise_capturing)]`T-langRelevant to the language teamdisposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions