Skip to content

Commit cb7f02d

Browse files
committed
Move back to an explicit annotation on traits and impls
1 parent c1981e4 commit cb7f02d

File tree

1 file changed

+40
-36
lines changed

1 file changed

+40
-36
lines changed

text/0000-const-trait-impls.md

+40-36
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ Make trait methods callable in const contexts. This includes the following parts
1515
Fully contained example ([Playground of currently working example](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=2ab8d572c63bcf116b93c632705ddc1b)):
1616

1717
```rust
18-
trait Default {
18+
const trait Default {
1919
(const) fn default() -> Self;
2020
}
2121

22-
impl Default for () {
22+
impl const Default for () {
2323
const fn default() {}
2424
}
2525

2626
struct Thing<T>(T);
2727

28-
impl<T: (const) Default> Default for Thing<T> {
28+
impl<T: (const) Default> const Default for Thing<T> {
2929
(const) fn default() -> Self { Self(T::default()) }
3030
}
3131

@@ -66,7 +66,7 @@ This RFC requires familarity with "const contexts", so you may have to read [the
6666

6767
Calling functions during const eval requires those functions' bodies to only use statements that const eval can handle. While it's possible to just run any code until it hits a statement const eval cannot handle, that would mean the function body is part of its semver guarantees. Something as innocent as a logging statement would make the function uncallable during const eval.
6868

69-
Thus we have a marker (`const`) to add in front of functions that requires the function body to only contain things const eval can handle. This in turn allows a `const` annotated function to be called from const contexts, as you now have a guarantee it will stay callable.
69+
Thus we have a marker, `const`, to add in front of functions that requires the function body to only contain things const eval can handle. This in turn allows a `const` annotated function to be called from const contexts, as you now have a guarantee it will stay callable.
7070

7171
When calling a trait method, this simple scheme (that works great for free functions and inherent methods) does not work.
7272

@@ -153,10 +153,11 @@ fn compile_time_default<T: const Default>() -> T {
153153

154154
### Conditionally const traits methods
155155

156-
Traits need to opt-in to allowing their impls to have const methods. Thus you need to prefix the methods you want to be const callable with `(const)`:
156+
Traits need to opt-in to allowing their impls to have const methods. Thus you need to mark the trait as `const` and prefix the methods you want to be const callable with `(const)`.
157+
Doing this at the same time is not a breaking change. Adding more `(const)` methods later is a breaking change (unless they are entirely new methods with default bodies).
157158

158159
```rust
159-
trait Trait {
160+
const trait Trait {
160161
(const) fn thing();
161162
}
162163
```
@@ -173,33 +174,34 @@ trait Trait {
173174
}
174175
```
175176

176-
and a result of this RFC would be that we would remove the attribute and add the `(const) fn` syntax for *methods*.
177+
and a result of this RFC would be that we would remove the attribute and add the `(const) fn` syntax for *methods* and the `const trait` syntax
178+
for trait declarations.
177179
Free functions are unaffected and will stay as `const fn`.
178180

179181
### Impls for conditionally const methods
180182

181-
Methods that are declared as `(const)` on a trait can now be made `const` in an impl:
183+
Methods that are declared as `(const)` on a trait can now be made `const` in an impl, if that impl is marked as `impl cosnt Trait`:
182184

183185
```rust
184-
impl Trait for Type {
186+
impl const Trait for Type {
185187
const fn thing() {}
186188
}
187189
```
188190

189-
If a single `(const)` method is declared as `const` in the impl, all `(const)` methods must be declared as such.
190-
It is still completely fine to just declare no methods `const` and keep the existing behaviour. Thus adding `(const)`
191-
methods to traits is not a breaking change. Only marking more methods as `(const)` if there are already some `(const)`
192-
methods is.
191+
193192

194193
### `const` methods and non-`const` methods on the same trait
195194

195+
If there is no `(const)` modifier on a method in a `const trait`, it is treated as any normal method is today.
196+
So `impl const Trait` blocks cannot mark them as `const` either.
197+
196198
```rust
197-
trait Foo {
199+
const trait Foo {
198200
(const) fn foo(&self);
199201
fn bar(&self);
200202
}
201203

202-
impl Foo for () {
204+
impl const Foo for () {
203205
const fn foo(&self) {}
204206
fn bar(&self) {
205207
println!("writing to terminal is not possible in const eval");
@@ -209,20 +211,20 @@ impl Foo for () {
209211

210212
### Conditionally-const trait bounds
211213

212-
Many generic `const fn` and especially many trait impls of traits with `(const)` methods do not actually require a const methods in the trait impl for their generic parameters.
214+
Many generic `const fn` and especially many `const trait`s do not actually require a const methods in the trait impl for their generic parameters.
213215
As `const fn` can also be called at runtime, it would be too strict to require it to only be able to call things with const methods in the trait impls.
214216
Picking up the example from [the beginning](#summary):
215217

216218
```rust
217-
trait Default {
219+
const trait Default {
218220
(const) fn default() -> Self;
219221
}
220222

221-
impl Default for () {
223+
impl const Default for () {
222224
const fn default() {}
223225
}
224226

225-
impl<T: Default> Default for Box<T> {
227+
impl<T: Default> const Default for Box<T> {
226228
fn default() -> Self { Box::new(T::default()) }
227229
}
228230

@@ -294,20 +296,20 @@ which we definitely do not support and have historically rejected over and over
294296

295297
### Impls with const methods for conditionally const trait methods
296298

297-
trait impls with const methods for generic types work similarly to generic `const fn`.
298-
Any `impl Trait for Type` is allowed to have `(const)` trait bounds if it has `const` methods:
299+
`const trait` impls for generic types work similarly to generic `const fn`.
300+
Any `impl const Trait for Type` is allowed to have `(const)` trait bounds:
299301

300302
```rust
301303
struct MyStruct<T>(T);
302304

303-
impl<T: (const) Add<Output = T>> Add for MyStruct<T> {
305+
impl<T: (const) Add<Output = T>> const Add for MyStruct<T> {
304306
type Output = MyStruct<T>;
305307
const fn add(self, other: MyStruct<T>) -> MyStruct<T> {
306308
MyStruct(self.0 + other.0)
307309
}
308310
}
309311

310-
impl<T> Add for &MyStruct<T>
312+
impl<T> const Add for &MyStruct<T>
311313
where
312314
for<'a> &'a T: (const) Add<Output = T>,
313315
{
@@ -332,16 +334,16 @@ struct MyStruct<T>(T);
332334
generates
333335

334336
```rust
335-
impl<T: PartialEq> PartialEq for MyStruct<T> {
337+
impl<T: (const) PartialEq> const PartialEq for MyStruct<T> {
336338
(const) fn eq(&self, other: &Rhs) -> bool {
337339
self.0 == other.0
338340
}
339341
}
340342

341-
impl<T: Eq> Eq for MyStruct<T> {}
343+
impl<T: (const) Eq> const Eq for MyStruct<T> {}
342344
```
343345

344-
For THIS RFC, we stick with `derive_const`, because it interacts with other ongoing bits of design work (e.g., RFC 3715)
346+
For this RFC, we stick with `derive_const`, because it interacts with other ongoing bits of design work (e.g., RFC 3715)
345347
and we don't want to have to resolve all design questions at once to do anything.
346348
We encourage another RFC to integrate const/unsafe and potentially other modifiers into the derive syntax in a better way.
347349
If this lands prior to stabilization, we should implement the const portion of it, otherwise we'll deprecate `derive_const`.
@@ -446,8 +448,10 @@ For closures and them implementing the `Fn` traits, see the [Future possibilitie
446448
You can make trait impls of many standard library traits for your own types have `const` methods.
447449
While it was often possible to write the same code in inherent methods, operators were
448450
covered by traits from `std::ops` and thus not avaiable for const contexts.
449-
Most of the time it suffices to add `const` before the methods in the impl block.
450-
The compiler will guide you and suggest where to also
451+
Most of the time it suffices to add `const` after the `impl`.
452+
453+
The compiler will then guide you and suggest where to also
454+
add `const` before methods and
451455
add `(const)` bounds for trait bounds on generic parameters of methods or the impl.
452456

453457
Similarly you can make your traits available for users of your crate to implement constly.
@@ -459,8 +463,8 @@ Note that this has two caveats that are actually the same:
459463
This is necessary as otherwise users of your crate may have impls where only some `(const)` methods from the trait
460464
have been marked as `const`, making that trait unusable in `const Trait` or `(const) Trait` bounds.
461465

462-
Most of the time it suffices to add `(const)` before all methods of your trait. The compiler will
463-
guide you and suggest where to also add `(const)` bounds for super trait bounds or trait bounds
466+
Most of the time it suffices to add `(const)` before all methods of your trait `const` before the `trait` keyword.
467+
The compiler will guide you and suggest where to also add `(const)` bounds for super trait bounds or trait bounds
464468
on generic parameters of your trait or your methods.
465469

466470
It should be rare that you are marking some methods as `(const)` and some not, and such unusual cases should
@@ -508,9 +512,9 @@ Everywhere where non-const trait bounds can be written, but only for traits that
508512

509513
* `const fn`
510514
* `(const) fn`
511-
* trait impls of traits with `(const)` methods
515+
* `impl const Trait` blocks
512516
* NOT in inherent impls, the individual `const fn` need to be annotated instead
513-
* `trait` declarations with `(const)` methods
517+
* `const trait` declarations
514518
* super trait bounds
515519
* where bounds
516520
* associated type bounds
@@ -657,20 +661,20 @@ We may relax this requirement in the future or make it implied.
657661
It is legal to add `(const)` to `Drop` impls' bounds, even thought the struct doesn't have them:
658662

659663
```rust
660-
trait Bar {
664+
const trait Bar {
661665
(const) fn thing(&mut self);
662666
}
663667

664668
struct Foo<T: Bar>(T);
665669

666-
impl<T: (const) Bar> Drop for Foo<T> {
670+
impl<T: (const) Bar> const Drop for Foo<T> {
667671
const fn drop(&mut self) {
668672
self.0.thing();
669673
}
670674
}
671675
```
672676

673-
There is no reason (and no coherent representation) of adding `(const)` trait bounds to a type.
677+
There is currently no reason (and no coherent representation) of adding `(const)` trait bounds to a type.
674678
Our usual `Drop` rules enforce that an impl must have the same bounds as the type.
675679
`(const)` modifiers are special here, because they are only needed in const contexts.
676680
While they cause exactly the divergence that we want to prevent with the `Drop` impl rules:
@@ -682,7 +686,7 @@ a type can be declared, but not dropped, because bounds are unfulfilled, this is
682686
Extraneous `(const) Trait` bounds where `Trait` isn't a bound on the type at all are still rejected:
683687

684688
```rust
685-
impl<T: (const) Bar + (const) Baz> Drop for Foo<T> {
689+
impl<T: (const) Bar + (const) Baz> const Drop for Foo<T> {
686690
const fn drop(&mut self) {
687691
self.0.thing();
688692
}

0 commit comments

Comments
 (0)