Niko's proposal made me rethink how I conceive of effects. Allow me to riff on a mental model focused on subtractive effects like const
, nopanic
, noalloc
, nothreads
etc.
Effects, like lifetimes, could be inferred whole-program; we choose not to. A signature is therefore a contract: by writing const fn
, I:
const impl
is similar: I commit to a bound on this impl and future changes of it, which you can therefore rely on.
Without traits, all is easy. The fun starts with traits:
const fn method();
);const impl Default for u32
);T: const Default
);~const fn foo<T: ~const Default>()
);Drop
is normally implicit but we must be able to talk about its runtime behavior; T: const Destruct
and T: ~const Destruct
serve this purpose.Design decision: trait bounds cannot talk about the behaviors of individual methods (RTN-like), they can only talk about a global bound on all method behaviors.
(I think!)
const fn
can have T: ~const Trait
bounds, and declares it will never do runtime behavior outside of the behavior of the trait methods on such T
s;T: const Trait
bound requires an impl that declared none of its methods would ever do runtime operations;
const
, just like normal fns. This is orthogonal to constness of the trait itself;const trait Trait
can have T: ~const OtherTrait
bounds, and declares that none of its default methods will ever do runtime behavior outside of the behavior of the methods from the ~const OtherTrait
bounds.
const impl Trait for X
can have T: ~const OtherTrait
bounds, and declares that none of its methods will ever do runtime behavior outside of the behavior of the methods from the ~const OtherTrait
bounds.
const trait
.Niko's proposal doesn't quite fit this model, because it allows methods to opt-out of the bound on the whole trait/impl.
~const fn
?This raises a question: what's the point of writing ~const fn
in Niko's proposal?
Can we somehow make const fn
be a usefully stronger commitment than ~const fn
? (Note that this model in this doc does not allow for "function that is only callable at compile-time").
const trait
?const trait
is the most confusing part of this. One may think it constrains impls or methods but no! It's only about default methods. I 100% expect users to assume that "const trait
=> the methods are const
".
Even this shuold be fine:
nopanic
would be awesome for unsafe code, the ability to temporarily move out of &mut T
, and linear types.
I can imagine other bounds being useful for unsafe code in general, very curious if y'all have ideas. See Zulip thread.
The real question is:
Should nopanic
extend to the trait bound? Is it ok for this function to panic if default()
panics? Is it ok if this function can't call default()
?? Is this like imperfect derive???
-> maybe intuition pump with nopanic Trait<T>
, do we expect propagation to bounds on T
?
-> maybe the real question is what happens with multiple effects:
would be madness to replicate the bounds.
In which cases to I actually want the unconstrained bound? If I'm not calling a method.