There are currently discussions how to use traits in const contexts. This reminded me of the recently accepted RFC: Overconstraining and omitting unsafe in impls of unsafe trait methods. This concept is called over-constraining because it allows trait impls to have more constraints than the trait. When applied to constness, it means that a trait function could be const
in the impl but not in the trait:
struct Foo;
impl Add for Foo {
type Output = Foo;
const fn add(self, rhs: Foo) -> Foo {
Foo
}
}
const fn bar() {
let _ = Foo + Foo;
}
Since over-constraining for safety has already been accepted, so it's only natural to apply it to constness as well.
The problem is that this is doesn't work for generic types. So I had the idea to create constrained traits which are identical to another trait but with additional constraints. Here's a possible syntax:
trait Foo {
type Ty;
unsafe fn f1();
fn f2(self) -> Self::Ty;
fn f3();
}
trait Bar constrains Foo {
type Ty: ToString;
fn f1();
const fn f2(self) -> Self::Ty;
}
const fn usage(bar: impl Bar) -> impl ToString {
bar.f2()
}
So when Bar
is required somewhere, any Foo
can be used, as long as
-
Ty
implementsToString
-
f1
is safe -
f2
is const
Bar
doesn't need to be explicitly implemented; any type that implements Foo
with the additional above constraints also implements Bar
.
This has several advantages:
- Not all trait functions must be const. For example, this makes it possible to use
Iterator
s in const functions where only thenext
method is const. - Non-const functions can be added to traits backwards-compatibly
- It could be useful in other areas unrelated to const functions. Over-constraining could be applied to safety, constness, types, trait/lifetime bounds and possibly more in the future.
Obviously the biggest drawback is the boilerplate needed to write a generic const function. Still I'd like to hear your opinions on this! Note that this is still in the "brainstorming" phase and not a finished proposal