Git Product home page Git Product logo

Comments (11)

traviscross avatar traviscross commented on July 29, 2024 7

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.

from rust.

tmandry avatar tmandry commented on July 29, 2024 3

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.

from rust.

tmandry avatar tmandry commented on July 29, 2024 2

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

from rust.

tmandry avatar tmandry commented on July 29, 2024 1

One additional argument that was raised in the meeting, but never really written out as an example, was the use of use<> as a bound on GATs. This really cemented for me the value in thinking of it as a bound. As a quick sketch, the bound could go in the trait:

trait BufferedIterator<A: Arena> {
    type Item<'a>: use<'a>;
    fn next<'a>(&'a mut self, a: &'a mut A) -> Self::Item<'a>;
}

or in use sites:

fn foo(iter: impl BufferedIterator<MyArena, for<'a> Item<'a>: use<'a>>) { ... }

Obviously this is a pretty niche and advanced feature, but it seems to make sense conceptually.

from rust.

rfcbot avatar rfcbot commented on July 29, 2024

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.

from rust.

pnkfelix avatar pnkfelix commented on July 29, 2024

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

from rust.

rfcbot avatar rfcbot commented on July 29, 2024

🔔 This is now entering its final comment period, as per the review above. 🔔

from rust.

pnkfelix avatar pnkfelix commented on July 29, 2024

Also, I do not necessarily agree with "Note, if we were later to support multiple use<..> items in a list of bounds, that use + use would represent a union, and so use + use would not be equivalent to use<T, U>." ; but I do not see us as committing to a union (vs intersection) path via our FCP here, so I'm willing to check my box and not make this a formal concern here.

from rust.

nikomatsakis avatar nikomatsakis commented on July 29, 2024

@rfcbot reviewed

I concur with @pnkfelix and I specifically want us to leave room for different interpretations of use in the future. There are two that I think make sense, described below. However, thanks to the restrictions of "at most one use" and "must include all generics that appear elsewhere in the bounds", both of these interpretations are equivalent, and I think that's fine for the moment.

To explain the two options, let's start by defining a conceptual bound use_only<G..> that is implemented for all types that reference the generics in G... (but not generics outside that set). That is the right formal mechanism and what e.g. I expect a-mir-formality to think about. Lacking a use_only bound therefore implies any type could be used.

One option then is to desugar use to use_only. This has the counterintuitive implication that use<A> + use<B> is equivalent to use<> which seems surprising.

An alternative option is to have a more complex desugaring where use appears in the surface syntax but not in the internal representation. We would translate a "user-facing" set of bounds as follows. If there is a use<G..> at all, then we take the union G.. of all generics that appear in the bounds anywhere and add use_only<G..>. Otherwise we don't add anything at all. In this way use<A, B> is equivalent to use<A> + use<B>: both would desugar to a single use_only<A, B>.

I think the second rule is more intuitive but also brings up mild concerns. Specifically we try to avoid "discontinuities" like this, where having no uses is "different" than having one or more uses. These kind of rules can have composability problems, but in this instance it might be that it's more composable. Unclear.

Anyway, we don't have to decide this now, but I do want to leave room for it as a possible future direction (which we may never need).

from rust.

Dirbaio avatar Dirbaio commented on July 29, 2024

I've got one concern with the + use<> syntax.

If we make use<> a bound, then it makes sense that it only restricts the hidden type further. That's what bounds do after all. Adding a bound can only make types no longer be accepted, it can't make a previously-not-accepted type start being accepted.

In Edition 2024, the default is to capture everything in scope. So, use<> is indeed only restricting. Without use<> the hidden type is allowed to capture anything, with use<> it's only allowed to capture the listed variables. Makes sense.

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


IMO making use<> a bound feels somewhat wrong, due to:

  • It doesn't make sense in Edition <2021 (see above)
  • Having multiple use<> doing intersection and not union is unintuitive.
  • Artificially disallowing use<> + use<> is also strange, no other bound in Rust behaves like that today.

I think the use<> impl Trait syntax makes much more sense. The way I see it, an opaque type is

  • a list of captures
  • a list of bounds

The list of captures is either automatically inferred (with different rules in <=2021 and >=2024), or explicitly specified with use<>.

Under this mental model, it makes sense for use<> to be a "modifier" applied to the impl keyword, and not a bound. So, use<> impl Trait syntax .The result makes sense for both <=2021 and >=2024 editions, and avoids the weirdness of two use<>s by only allowing one.

from rust.

traviscross avatar traviscross commented on July 29, 2024

The intuition I'd suggest is that, if a bounds list contains a use<..> bound, that fully specifies the set of captures, and if it doesn't, then the compiler adds one in lowering that lists all of the generics captured under the rules for that edition.

That is, I'd suggest thinking about it in terms of the use<..> bound always being present, conceptually, and that there's a convenient elision rule.

from rust.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.