Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Keyword unreservations (pure, sizeof, alignof, offsetof) #2421

Merged
merged 10 commits into from
May 27, 2018
302 changes: 302 additions & 0 deletions text/0000-unreservations-2018.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
- Feature Name: `unreservations`
- Start Date: 2018-04-26
- RFC PR:
- Rust Issue:

# Summary
[summary]: #summary

We unreserve:
+ `pure`
+ `unsize`
+ `sizeof`
+ `alignof`
+ `offsetof`
+ `priv`

# Motivation
[motivation]: #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
[guide-level-explanation]: #guide-level-explanation

See the [reference-level-explanation].

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

[list of reserved keywords]: https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html#keywords-currently-in-use

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`
+ `unsize`
+ `sizeof`
+ `alignof`
+ `offsetof`
+ `priv`

# Drawbacks
[drawbacks]: #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
[alternatives]: #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.

[applicative]: http://hackage.haskell.org/package/base-4.11.1.0/docs/Control-Applicative.html#t:Applicative

When *generic associated types* (GATs) lands, it is likely that people would
like to use this in their [applicative functor][applicative] and monad libraries,
which speaks in favor of unreserving `pure`. This use case explicitly mentioned by [`@ubsan`](https://github.com/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

```rust
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*

```rust
fn foo() -> Result<i32, Error> {
if bar() {
pure 0;
}
...
}
```

desugars into:

```rust
fn foo() -> Result<i32, Error> {
if bar() {
return Try::from_ok(0);
}
...
}
```

[Applicative laws]: https://en.wikibooks.org/wiki/Haskell/Applicative_functors#Applicative_functor_laws

While you might think that Haskell developers would be in favor of this,
that does not seem to be the case. Haskell developers over at
`#haskell @ freenode` were not particularly in favor 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 `unsize`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @eddyb - Double checking: This is fine?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsized may be useful for custom DST, so I think it's worth holding onto for now

Copy link
Contributor Author

@Centril Centril Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikeyhew Can you elaborate a bit? (possibly with a code example?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use dyn instead of unsize for DST.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikeyhew No specific use case; your comment helps me refine the RFC tho :) But @kennytm's idea seems nice? If this discussion doesn't develop I'll strike unsized in a bit with your motivation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use dyn for dynamic existentials, not dynamic sizes, e.g. you could imagine [T] expanding to dyn<n: usize> [T; n] and dyn Trait to dyn<T: Trait> T.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eddyb But what about let vla = [T; dyn size];?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now scratched unsized given @mikeyhew's motivation and that unreserving it violates:

[..] we are sure that we have no intention of using the keyword in the future [..]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Centril That's not a size, that's a length. Dynamic existential shorthands (when each bound type/value name is used only once, in an unambiguous way) could be dyn Trait and [T; dyn usize] (or just dyn, since the type can be inferred or specified elsewhere).


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`.

## Rationale for `sizeof`, `alignof`, and `offsetof`

We already have [`std::mem::size_of`](https://doc.rust-lang.org/nightly/std/mem/fn.size_of.html) and similar which
are `const fn`s or can be.
Copy link

@hanna-kruppe hanna-kruppe Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offsetof can't be a const fn, at least not without some sort of static reflection being added to the language – which is a huge feature and AFAIK nothing specific in this direction has ever been proposed. I agree it could be unreserved (and implemented as a macro, as @eddyb showed), but this rationale here only applies to sizeof and alignof, not offsetof.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I would prefer if the macro generated a const fn, so you could use it anywhere a constant value was expected. Most of it will soon become possible, with the exception of the assert, although that's mostly a sanity check and shouldn't be required for soundness.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rkruppe In theory we could know the delta between fields of the same structure (or from the start of the structure to one field) statically at compile time, without needing full static reflection. And many uses of offsetof in C want it in a compile-time-constant context..

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshtriplett What you describe sounds more like a special keyword than a const fn! I don't see a nice, generic (i.e., which enables more than just const fn offsetof) extension to current Rust that allows one to write const fn offsetof in a library. Well, nothing that's smaller/simpler than the beginnings of a reflection system. Once you start introducing things like "reifying a field into a value and passing it to a function" (which seems necessary to even define the interface of const fn offsetof, let alone its implementation) you're already in reflection land.

(I agree that offset calculations need to work in constant contexts, but as @eddyb said, the macro is perfectly compatible with that!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rkruppe This is true; I'll clarify.


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.

## Rationale for `priv`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @withoutboats - You wanted to keep this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to keep this!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@petrochenkov Could you elaborate a lil bit on possible use cases you foresee?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering how much activity we have around module proposals, including visibility, I think holding onto this one seems reasonable for now.

Copy link
Contributor Author

@Centril Centril Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aight. As we discussed on the lang team meeting; I'll scratch this one of the list in this round of unreservations. We can always revisit later.


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

```rust
priv struct Foo;
pub struct Bar {
priv baz: u8
}
```

Since everything is already private by default, `priv` would only be an extra
hint that users can use to be more explict, but serves no other purpose.
Further, we could possibly use `pub(self)` for `priv` instead.

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

```rust
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.

For these reasons, the current proposal is to unreserve.

# Prior art
[prior-art]: #prior-art

Not applicable.

# Unresolved questions
[unresolved]: #unresolved-questions

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

# Appendix
[appendix]: #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](https://github.com/rust-lang/rfcs/pull/1888).

- `typeof` - We might want this for hypothetical usages such as:
```rust
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:
```rust
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:

```rust
impl TR for S {
delegate * to f;

#[override(from="f")]
fn foo(&self) -> u32 {
42
}
}
```

which we could rewrite as:

```rust
impl TR for S {
delegate * to f;

override(from f) fn foo(&self) -> u32 {
42
}
}
```

## Possible future unreservations

### `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:

```rust
dyn fn foo(..) -> whatever { .. }
```

instead of:

```rust
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` ](https://internals.rust-lang.org/t/forever-immutable-owned-values/6807).
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`.