Summary

We unreserve:

  • pure
  • sizeof
  • alignof
  • offsetof

Motivation

We are currently not using any of the reserved keywords listed in the summary for anything in the language at the moment. We also have no intention of using the keywords for anything in the future, and as such, we want to unreserve them so that rustaceans can use them as identifiers.

Guide-level explanation

See the reference-level-explanation.

Reference-level explanation

The keywords listed below are removed from the list of reserved keywords and are longer reserved such that they can be used as general identifiers. This is done immediately and on edition 2015.

The keywords to unreserve are:

  • pure
  • sizeof
  • alignof
  • offsetof

Drawbacks

The only drawback is that we're not able to use each listed word as a keyword in the future, without a reservation in a new edition, if we realize that we made a mistake.

See the rationale for potential risks with each keyword.

Rationale and alternatives

There's only one alternative: Not unreserving all listed / some keywords.

Not unreserving a keyword would make the word unavailable for use as an identifier.

General policy around unreservations

This RFC establishes a general rationale and policy for keyword unreservation: If we are not using a keyword for anything in the language, and we are sure that we have no intention of using the keyword in the future, then it is permissible to unreserve a keyword and it is motivated. Additionally, if there is a desire for a keyword to be used as an identifier, this can in some cases outweigh very hypothetical and speculative language features.

Rationale for pure

This keyword used to be used for pure fn, that is: as an effect.

When generic associated types (GATs) lands, it is likely that people would like to use this in their applicative functor and monad libraries, which speaks in favour of unreserving pure. This use case explicitly mentioned by @ubsan who requested that the keyword be unreserved for this purpose.

Potential drawbacks

Examples / The reasons why we might want to keep pure reserved are:

1. Effects


# #![allow(unused_variables)]
#fn main() {
pure fn foo(x: Type) -> Type {
    ...
}
#}

Here, pure denotes a deterministic function -- but we already have const for more or less the same, and it is unlikely that we would introduce an effect (or restriction thereof) that is essentially const fn but not entirely. So this use case is unlikely to happen.

2. Explicit Ok-wrapping


# #![allow(unused_variables)]
#fn main() {
fn foo() -> Result<i32, Error> {
    if bar() {
        pure 0;
    }
    ...
}
#}

desugars into:


# #![allow(unused_variables)]
#fn main() {
fn foo() -> Result<i32, Error> {
    if bar() {
        return Try::from_ok(0);
    }
    ...
}
#}

While you might think that Haskell developers would be in favour of this, that does not seem to be the case. Haskell developers over at #haskell @ freenode were not particularly in favour of this use as pure in this context as pure does not respect the Applicative laws. The desugaring is also not particularly obvious when pure is used. If we did add sugar for explicit Ok-wrapping, we'd probably go with something other than pure.

Summary

In both 1. and 2., pure can be contextual. We also don't think that the drawbacks are significant for pure.

Rationale for sizeof, alignof, and offsetof

We already have std::mem::size_of and similar which are const fns or can be. In the case of offsetof, we would instead use a macro offset_of!.

A reason why we might want to keep these reserved is that they already exist in the standard library, and so we might not want anyone to define these functions, not because we will use them ourselves, but because it would be confusing, and so the error messages could be improved saying "go look at std::mem::size_of instead". However, we believe it is better to allow users the freedom to use these keywords instead.

Prior art

Not applicable.

Unresolved questions

There are none. All reservations we will do should be resolved before merging the RFC.

Appendix

Reserved keywords we probably don't want to unreserve

The following keywords are used in the nightly compiler and we are sure that we want to keep them:

  • yield - Generators
  • macro - Macros 2.0

Additionally, there are known potential use cases / RFCs for:

  • become - We might want this for guaranteed tail calls. See the postponed RFC.

  • typeof - We might want this for hypothetical usages such as:

    
    # #![allow(unused_variables)]
    #fn main() {
    fn foo(x: impl Bar, y: typeof(x)) { .. }
    #}
  • do - We might want this for two uses:

    1. do { .. } while cond; loops.
    2. Haskell style do notation: let az' = do { x <- ax; y <- ay(x); az };.
  • abstract - We might/would like this for:

    
    # #![allow(unused_variables)]
    #fn main() {
    abstract type Foo: Copy + Debug + .. ;
    #}
  • override - This could possibly used for:

    • OOP inheritance -- unlikely that we'll get such features.

    • specialization -- we do not annotate specialization on the overriding impl but rather say that the base impl is specializable with default, wherefore override does not make much sense.

    • delegation -- this usage was proposed in the delegations pre-RFC:

      
      # #![allow(unused_variables)]
      #fn main() {
      impl TR for S {
          delegate * to f;
      
          #[override(from="f")]
          fn foo(&self) -> u32 {
              42
          }
      }
      #}

      which we could rewrite as:

      
      # #![allow(unused_variables)]
      #fn main() {
      impl TR for S {
          delegate * to f;
      
          override(from f) fn foo(&self) -> u32 {
              42
          }
      }
      #}

Possible future unreservations

unsized

This would be a modifier on types, but we already have <T: ?Sized> and we could have T: !Sized so there seems to be no need for keeping unsized.

However, unsized type or unsized struct might be a desirable syntax for declaring a dynamically sized type (DST) or completely unsized type. Therefore, we will hold off on unreserving unsized until we have a better ideas of how custom DSTs will work and it's clear we don't need unsized as a keyword.

priv

Here, priv is a privacy / visibility modifier on things like fields, and items. An example:


# #![allow(unused_variables)]
#fn main() {
priv struct Foo;
pub struct Bar {
    priv baz: u8
}
#}

Since fields are already private by default, priv would only be an extra hint that users can use to be more explicit, but serves no other purpose. Note however that enum variants are not private by default. Neither are items in traits. Annotating items as priv in traits could potentially be useful for internal fns used in provided fn implementations. However, we could possibly use pub(self) instead of priv.

Permitting priv could also be confusing for readers. Consider for example:


# #![allow(unused_variables)]
#fn main() {
pub struct Foo {
    priv bar: T,
    baz: U,
}
#}

An unsuspecting reader can get the impression that bar is private but baz is public. We could of course lint against this mixing, but it does not seem worth the complexity.

However, right now (2018-04-26), there is a lot of movement around the module system. So we would like to wait and discuss unreserving this keyword at some later time.

box

We use this in nightly for box patterns. We might want to unreserve this eventually however.

virtual

This annotation would be for something like virtual functions (see dyn). However, we already have dyn, so why would we need virtual? Assuming the following makes sense semantically (which we do not care about here), we could easily write:


# #![allow(unused_variables)]
#fn main() {
dyn fn foo(..) -> whatever { .. }
#}

instead of:


# #![allow(unused_variables)]
#fn main() {
virtual fn foo(..) -> whatever { .. }
#}

However, there might be some use case related to specialization. After specialization is stable, we would like to revisit unreservation of virtual.

final

The final keyword is currently reserved. It is used in Java to mean two separate things:

  1. "you can't extend (inheritance) this class",
  2. "you can't mutate this variable", which we already have for let bindings by default.

A possible use for final for us might be for Frozen . However, Frozen does not have many known uses other than for users who want to be more strict about things. The word final might not be what Java users would expect it to mean in this context, so it's probably not a good keyword for Frozen.

However, there might be some use case related to specialization. After specialization is stable, we would like to revisit unreservation of final.