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

Support underscores as constant names #2526

Merged
merged 2 commits into from Oct 8, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
120 changes: 120 additions & 0 deletions text/0000-const-wildcard.md
@@ -0,0 +1,120 @@
- Feature Name: `const_wildcard`
- Start Date: 2018-08-18
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

Allow assigning constants to `_`, as in `const _: TYPE = VALUE`, analogous to
`let _ = VALUE`.

# Motivation
[motivation]: #motivation

The ability to ensure that code type checks while discarding the result is
useful, especially in custom derives. For example, the following code will not
compile if the type `MyType` doesn't implement the trait `MyTrait`:

```rust
const _FOO: () = {
use std::marker::PhantomData;
struct ImpelementsMyTrait<T: MyTrait>(PhantomData<T>);
let _ = ImplementsMyTrait::<MyType>(PhantomData); // type checking error if MyType: !MyTrait
()
};
```

Unfortunately, this requires coming up with a unique identifier to assign to.
This is error-prone because no matter what identifier is chosen, there's always
a possibility that a user will have already used the same identifier in their
code. If writing `const _: () = { ... }` were valid, then this would be a
non-issue - the `const _` could be repeated many times without conflicting with
any other identifier in scope.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

Allow assigning to `_` when defining a new constant. Just like `let _`, this
doesn't introduce any new bindings, but still evaluates the rvalue at compile
time like any other constant.
Copy link
Contributor

Choose a reason for hiding this comment

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

A guide-level explanation does not include the word "rvalue", IMO. And this doesn't explain what the point of the feature is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a phrase you'd suggest instead of "rvalue"?

The point of the feature is described in the "Motivation" section.

Copy link
Contributor

Choose a reason for hiding this comment

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

What C and C++ call an rvalue, we call a value expression. See the reference on 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 think we've called it rvalue for some time as well.. see https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md.

Choose a reason for hiding this comment

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

We used to call it rvalue, but we've wisened up since then. Likewise with lvalues (now places).

Copy link
Contributor

Choose a reason for hiding this comment

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

@rkruppe you speak the truth :)


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

The following changes are made to the language:

## Grammar

The grammar of `item_const` is changed from:

```text
item_const : CONST ident ':' ty '=' expr ';' ;
```

to:

```text
item_const : CONST (ident | UNDERSCORE) ':' ty '=' expr ';' ;
```

## Type checking

When type checking an associated `const` item, the token `_` may not occur as
the name of the item.

When type checking a `const` item not inside an `impl` item, the token `_` is
permitted as the name of such an item. When that token does occur, it is
replaced with a freshly generated and unique identifier.

# Drawbacks
[drawbacks]: #drawbacks

The rules around constant identifiers are made somewhat more complicated, as is
the compiler logic for handling them. A distinction is introduced between
associated `const` items (inside `impl`s) and non-associated `const` items.

# Rationale and alternatives
[alternatives]: #alternatives

## Rationale

This would allow more ergonomic uses of a number of patterns used today:
- Ensuring that types have certain trait bounds in custom derives, as explained
in the [Motivation] section.
- [`const_assert!`](https://docs.rs/static_assertions/0.2.5/static_assertions/macro.const_assert.html)
and other macros in the
[`static_assertions`](https://docs.rs/static_assertions/0.2.5/static_assertions/index.html)
crate, which currently work only in a scope (so that they can use a `let`
binding) or requires the user to specify a scope-unique name for a function
which will be used to contain the expression that is the meat of the macro.

Eventually, we will likely want to support fully general pattern matching just
like in `let` bindings (e.g., `const (a, b): (u8, u8) = (1, 1)`) to not have
`const _` be a special case in the language. However, this RFC leaves the
details of such a design up to a future RFC.

## Alternatives

- We could provide procedural macros with an API that fetches a new,
globally-unique identifier.
- We could support anonymous modules (`mod { ... }` or `mod _ { ... }`).
- We could support anonymous top-level functions (`fn _() { ... }`).

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

Go allows unnamed constants using the syntax `const _ = ...`. It also allows
top-level variable bindings which are evaluated at init time, before `main` is
run - `var _ = ...`. This latter syntax is often used to ensure that a
particular type implements a particular interface, as in this example [from the
standard library](https://golang.org/src/math/big/ftoa.go#L379):

```go
var _ fmt.Formatter = &floatZero // *Float must implement fmt.Formatter
```

# Unresolved questions
[unresolved]: #unresolved-questions

None.