You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a tracking issue for trait bounds on const fns. The feature gate for the issue is #![feature(const_fn_trait_bound)]. This is not to be confused with the "trait impl must be const in const contexts" syntax, ~const Trait, which is a part of const_trait_impl and is tracked in #67792.
About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
This has been stabilized in #93827, I'd really prefer if we could take a step back, revert that stabilization and consider an edition-based flow for getting ?const instead of ~const afterall:
The few currently allowed bounds in const fn could be transitioned to ?const via deprecation (plus more ?const bounds allowed) in this edition, then the next edition can change the meaning of trait bounds without ?const in const fn and we can have the thing we originally wanted.
I've voiced this idea on the tracking issue for the impl const Trait RFC earlier: #67792 (comment)
Reacted by Christopher Serr, rodrimati1992 and Brad Gibson
Reacted by Caio, Taiki Endo, René Léveillé and Jubilee
It's not just the bounds, it's also function pointers and trait objects that already made me advocate for this change long before the bounds stabilization hole was discovered.
A T: Trait bound should be symmetric with &dyn Trait, so if the former implicitly gains constness so should the latter. However, this is stable since forever:
What's wrong with having the stabilization that's already landed and switching to ?const in a future edition?
Then you'd be stabilizing something just to deprecate it short after. But I guess it would be fine.
It's not just the bounds, it's also function pointers and trait objects that already made me advocate for this change long before the bounds stabilization hole was discovered.
I think I've seen this argument before but forgot about it. To me personally, with how different and magic dyn already is, I'm not convinced by this argument but I can see where you're coming from.
The last two are the same by definition; the first two IMO should be the same as everything else would be really confusing.
If you consider dyn too much magic, let me note that this also affects const fn foo(x: fn()), but the syntactic symmetry is more striking with dyn. These (traits bounds, dyn, fn) are all just different ways to "abstract over code", and IMO their interaction with const should be consistent across the board.
If we go with the ~const approach, all of these will not impose additional requirements for the callers.
If we do ?const, some of them will be inconsistent, even if we made it consistent for the next edition it is going to be a pain to migrate, because all types (including fn pointers) now needs ?const to have the same behavior as before.
When you are arguing for ?const you are arguing for the fact that all of const trait bounds, const dyn traits, const impl traits, const fn pointers are used more frequently than their non-const versions in const contexts.
If we do ?const, some of them will be inconsistent,
Well, we could make all of them require ?const, but that is super backwards incompatible.
(In const fn, fn and dyn are unstable -- or just have recently been stabilized together with the trait bounds. However, in const/static they are stable and having the same type mean something different in const vs const fn sounds like a Bad Idea.)
The last two are the same by definition; the first two IMO should be the same as everything else would be really confusing.
I thought about this for a while, and I am not convinced for two reasons:
First, I don't see why you would ever use dyn Trait in a const fn but second and more importantly, I can now articulate this "dyn Trait is just inherently different" feeling I had before. Take the case of auto traits:
// Return type is !Send !Sync even if the type underpinning `x` isconstfnfoo(x:&dynTrait) -> &dynTrait{ x }// Return type Send and/or Sync if the type of `x` isconstfnfoo(x:implTrait) -> implTrait{ x }
It's not something people think about a lot, but once you do it's actually intuitive (IMHO): Because of the type erasure, you can't propagate auto traits.
Additionally, and this has been stated before but I think it's important to bring it up again: Requiring a const impl in a const context is simply the way more useful default, and it's a backwards-compatible change to add ?const. People don't need to learn about all of this concept of "needs to be const impl if called in const context" until they actually need itm or come across an API that uses ?const.
Finally, and this isn't really a strong argument for either side I think, but I feel like I should point out that there are conceptually three cases for trait bounds in generic const fn. This could probably be seen two ways, let me try to explain with a table:
I don't see why you would ever use dyn Trait in a const fn
For the same reasons you would use dyn Trait in a regular fn.
Return type Send and/or Sync if the type of x is
Yeah, return-type impl Trait is semi-transparent, and in particular is transparent for auto traits. That's an odd quirk, but nothing fundamental and not conceptually very relevant in my eyes.
Requiring a const impl in a const context is simply the way more useful default,
I agree! IMO that also applies to dyn Trait and fn types.
But I don't agree strong enough to be willing to sacrifice consistency for this.
there are conceptually three cases for trait bounds in generic const fn
Yes, I agree.
The "less weird" column however is inconsistent with how ? syntax is used elsewhere -- with ?Sized, we have that writing nothing is equivalent to having a Sized bound. So I would expect that with ?const, this indicates opting out of a default const, so explicitly writing const should be the same as writing nothing.
What this goes to show is that the situation is fundamentally different from ?Sized.
Requiring a const impl in a const context is simply the way more useful default,
I agree! IMO that also applies to dyn Trait and fn types.
Oh, I think I see. Theoretically we could have this behavior even for non-generic const fn, there's no technical reasons preventing it. I didn't really consider that.
with ?Sized, we have that writing nothing is equivalent to having a Sized bound. So I would expect that with ?const, this indicates opting out of a default const
Heh, so I guess this gives rise to yet another possible (but to me very unlikely) option:
Activity
Rollup merge of rust-lang#93682 - PatchMixolydic:where-in-the-world-i…
Rollup merge of rust-lang#93682 - PatchMixolydic:where-in-the-world-i…
Rollup merge of rust-lang#93682 - PatchMixolydic:where-in-the-world-i…
Rollup merge of rust-lang#93682 - PatchMixolydic:where-in-the-world-i…
Rollup merge of rust-lang#93682 - PatchMixolydic:where-in-the-world-i…
fee1-dead commentedon Feb 7, 2022
cc @rust-lang/wg-const-eval: Should we remove this feature because of #90912?
oli-obk commentedon Feb 7, 2022
Yes, together with function pointers and trait objects. We just need a stabilization PR and a summary for the lang team
eholk commentedon Feb 11, 2022
I have a stabilization PR at #93827 and a documentation PR now at rust-lang/reference#1166.
Rollup merge of rust-lang#93827 - eholk:stabilize-const_fn-features, …
jplatte commentedon Mar 22, 2022
This has been stabilized in #93827, I'd really prefer if we could take a step back, revert that stabilization and consider an edition-based flow for getting
?constinstead of~constafterall:The few currently allowed bounds in
const fncould be transitioned to?constvia deprecation (plus more?constbounds allowed) in this edition, then the next edition can change the meaning of trait bounds without?constinconst fnand we can have the thing we originally wanted.I've voiced this idea on the tracking issue for the
impl const TraitRFC earlier: #67792 (comment)jhpratt commentedon Mar 22, 2022
What's wrong with having the stabilization that's already landed and switching to
?constin a future edition?RalfJung commentedon Mar 22, 2022
It's not just the bounds, it's also function pointers and trait objects that already made me advocate for this change long before the bounds stabilization hole was discovered.
A
T: Traitbound should be symmetric with&dyn Trait, so if the former implicitly gainsconstness so should the latter. However, this is stable since forever:jplatte commentedon Mar 22, 2022
Then you'd be stabilizing something just to deprecate it short after. But I guess it would be fine.
I think I've seen this argument before but forgot about it. To me personally, with how different and magic
dynalready is, I'm not convinced by this argument but I can see where you're coming from.RalfJung commentedon Mar 22, 2022
To add to the argument, consider the syntactic parallel between
implanddyn:The last two are the same by definition; the first two IMO should be the same as everything else would be really confusing.
If you consider
dyntoo much magic, let me note that this also affectsconst fn foo(x: fn()), but the syntactic symmetry is more striking withdyn. These (traits bounds,dyn,fn) are all just different ways to "abstract over code", and IMO their interaction withconstshould be consistent across the board.fee1-dead commentedon Mar 22, 2022
consider this:
If we go with the ~const approach, all of these will not impose additional requirements for the callers.
If we do ?const, some of them will be inconsistent, even if we made it consistent for the next edition it is going to be a pain to migrate, because all types (including fn pointers) now needs ?const to have the same behavior as before.
When you are arguing for ?const you are arguing for the fact that all of const trait bounds, const dyn traits, const impl traits, const fn pointers are used more frequently than their non-const versions in const contexts.
RalfJung commentedon Mar 22, 2022
Well, we could make all of them require
?const, but that is super backwards incompatible.(In
const fn,fnanddynare unstable -- or just have recently been stabilized together with the trait bounds. However, inconst/staticthey are stable and having the same type mean something different inconstvsconst fnsounds like a Bad Idea.)jplatte commentedon Mar 29, 2022
I thought about this for a while, and I am not convinced for two reasons:
First, I don't see why you would ever use
dyn Traitin aconst fnbut second and more importantly, I can now articulate this "dyn Traitis just inherently different" feeling I had before. Take the case of auto traits:It's not something people think about a lot, but once you do it's actually intuitive (IMHO): Because of the type erasure, you can't propagate auto traits.
Additionally, and this has been stated before but I think it's important to bring it up again: Requiring a
const implin a const context is simply the way more useful default, and it's a backwards-compatible change to add?const. People don't need to learn about all of this concept of "needs to be const impl if called in const context" until they actually need itm or come across an API that uses?const.Finally, and this isn't really a strong argument for either side I think, but I feel like I should point out that there are conceptually three cases for trait bounds in generic
const fn. This could probably be seen two ways, let me try to explain with a table:const implT: const Trait(?)T: const Trait(?)const implinconstcontextT: ~const TraitT: TraitimplT: TraitT: ?const TraitRalfJung commentedon Mar 30, 2022
For the same reasons you would use
dyn Traitin a regularfn.Yeah, return-type
impl Traitis semi-transparent, and in particular is transparent for auto traits. That's an odd quirk, but nothing fundamental and not conceptually very relevant in my eyes.I agree! IMO that also applies to
dyn Traitandfntypes.But I don't agree strong enough to be willing to sacrifice consistency for this.
Yes, I agree.
The "less weird" column however is inconsistent with how
?syntax is used elsewhere -- with?Sized, we have that writing nothing is equivalent to having aSizedbound. So I would expect that with?const, this indicates opting out of a defaultconst, so explicitly writingconstshould be the same as writing nothing.What this goes to show is that the situation is fundamentally different from
?Sized.jplatte commentedon Mar 30, 2022
Oh, I think I see. Theoretically we could have this behavior even for non-generic
const fn, there's no technical reasons preventing it. I didn't really consider that.Heh, so I guess this gives rise to yet another possible (but to me very unlikely) option:
const implT: const Trait(?)const implin const contextT: TraitorT: ~const TraitT: ?~const Traitoli-obk commentedon May 13, 2022
This has been stabilized in #93827