Adding new keyword generics
#![allow(unused)] fn main() { const fn foo() { // maybe const context let file = fs::open("hello").unwrap(); // compile error! => `fs::open` is not a maybe const function! } ~base fn foo() { // assume `const` as the default; invert the relationship let file = fs::open("hello").unwrap(); // compile error! => `fs::open` is // a base function which cannot be // called from a maybe base context } ~async fn foo() { let file = my_definitely_async_fn().await; // compile error! } }
fn foo<effect F: const>(f: impl F * Fn() -> ()) { f(); } fn foo<effect F: const>(f: impl effect<F> Fn() -> ()) { f(); } // compile error! // effect `F` is maximally inclusive! // missing `.await` // maximally inclusive effects are not forward compatible! - once // we add a new effect existing code will not compile! // The calling convention may change each time we add a new effect! fn main() { foo(some_fn); // Infer all effects to Not* }
Adding new effects to the language does not break anyone, because effects must be opted in. Adding a new effect to the opt-in effect generics of a function will break callers that infer the effect to be required.
Editions can add new effects to the list of defaults. This is not a breaking change because calling crates can stay on old editions, even if the lib crate got updated to a newer edition. THe lower edition crates don't see the defaults and turn them off.