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

Deprecate anonymous parameters #1685

Merged
merged 3 commits into from
May 1, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions text/0000-deprecate-anonymous-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
- Feature Name: deprecate_anonymous_parameters
- Start Date: 2016-07-19
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

Currently Rust allows anonymous parameters in trait methods:

```Rust
trait T {
fn foo(i32);

fn bar_with_default_impl(String, String) {

}
}
```

This RFC proposes to deprecate this syntax. This RFC intentionally does not
propose to remove this syntax.

# Motivation
[motivation]: #motivation

Anonymous parameters are a [historic accident]. They cause a number of technical
Copy link
Member

Choose a reason for hiding this comment

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

This claim is misrepresenting the text it references.

What Niko wrote there was:

I don't believe it was intended that parameter names can be omitted in trait methods with a body

(emphasis added)

I do not think it was ever an accident that the design allowed anonymous parameters for trait methods without a body.

Copy link
Member Author

Choose a reason for hiding this comment

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

I was referring to another part of the comment, but maybe I've misinterpreted it

I thought we planned to require parameter names in trait definitions for this reason -- but I guess that never happened?

@nikomatsakis what would be the right way to phrase this in the RFC? Or we can just drop this sentence: it does not add anything to the current situation, it just an interesting historical detail :)

Copy link
Member Author

@matklad matklad Mar 15, 2017

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Either way. I do remember discussing this at some point but -- as you say -- it never happened.

annoyances.

1. Surprising pattern syntax in traits

```Rust
trait T {
fn foo(x: i32); // Ok
fn bar(&x: &i32); // Ok
fn baz(&&x: &&i32); // Ok
fn quux(&&&x: &&&i32); // Syntax error
}
```

That is, patterns more complex than `_, foo, &foo, &&foo, mut foo` are
forbidden.

2. Inconsistency between default implementations in traits and implementations
in impl blocks

```Rust
trait T {
fn foo((x, y): (usize, usize)) { // Syntax error
}
}

impl T for S {
fn foo((x, y): (usize, usize)) { // Ok
}
}
```

3. Inconsistency between method declarations in traits and in extern blocks

```Rust
trait T {
fn foo(i32); // Ok
}

extern "C" {
fn foo(i32); // Syntax error
}
```

4. Slightly more complicated syntax analysis for LL style parsers. The parser
must guess if it currently parses a pattern or a type.

5. Small complications for source code analyzers (e.g. [IntelliJ Rust]) and
potential alternative implementations.

6. Potential future parsing ambiguities with named and default parameters
syntax.


None of these issues is significant, but they exist.


Even if we exclude these technical drawbacks, it can be argued that allowing to
omit parameter names unnecessary complicates the language. It is unnecessary
because it does not make Rust more expressive and does not provide noticeable
ergonomic improvements. It is trivial to add parameter name, and only a small
fraction of method declarations actually omits it.

Another drawback of this syntax is its impact on the learning curve. One needs
to have a C background to understand that `fn foo(T);` means a function with
single parameter of type `T`. If one comes from dynamically typed language like
Python or JavaScript, this `T` looks more like a parameter name.

Anonymous parameters also cause inconsistencies between trait definitions and
implementations. One way to write an implementation is to copy the method
prototypes from the trait into the impl block. With anonymous parameters this
leads to syntax errors.


[historic accident]: https://github.com/rust-lang/rust/pull/29406#issuecomment-151859611
[IntelliJ Rust]: https://github.com/intellij-rust/intellij-rust/commit/1bb65c47341a04aecef5fa6817e8b2b56bfc9abb#diff-66f3ba596f0ecf74a2942b3223789ab5R41


# Detailed design
[design]: #detailed-design


## Backward compatibility

Removing anonymous parameters from the language is formally a breaking change.
The breakage can be trivially and automatically fixed by adding `_:` (suggested by @nagisa):

```Rust
trait T {
fn foo(_: i32);

fn bar_with_default_impl(_: String, _: String) {

}
}
```

However this is also a major breaking change from the practical point of view.
Parameter names are rarely omitted, but it happens. For example,
`std::fmt::Display` is currently defined as follows:

```Rust
trait Display {
fn fmt(&self, &mut Formatter) -> Result;
}
```

Of the 5560 packages from crates.io, 416 include at least one usage of
an anonymous parameter ([full report]).

[full report]: https://github.com/rust-lang/rfcs/pull/1685#issuecomment-238954434


## Benefits of deprecation

So the proposal is just to deprecate this syntax. Phasing the syntax out of
usage will mostly solve the learning curve problems. The technical problems
would not be solved until the actual removal becomes feasible and
practical. This hypothetical future may include:

* Rust 2.0 release.
* A widely deployed tool to automatically fix deprecation warnings.
* Storing crates on crates.io in "elaborated" syntax independent format.

Enabling deprecation early makes potential future removal easier in practice.


## Deprecation strategy

There are two possible ways to deprecate this syntax:

### Hard deprecation

One option is to produce a warning for anonymous parameters. This is backwards
compatible, but in practice will force crate authors to actively change their
code to avoid the warnings, causing code churn.

### Soft deprecation

Another option is to clearly document this syntax as deprecated and add an
allow-by-default lint, a clippy lint, and an IntelliJ Rust inspection, but do
not produce compiler warnings by default. This will make the update process more
gradual, but will delay the benefits of deprecation.

### Automatic transition

Rustfmt and IntelliJ Rust can automatically change anonymous parameters to
`_`. However it is better to manually add real names to make it obvious what
name is expected on the `impl` side.

# Drawbacks
[drawbacks]: #drawbacks

* Hard deprecation will cause code churn.

* Soft deprecation might not be as efficient at removing the syntax from usage.

* The technical issues can not be solved nicely until the deprecation is turned
into a hard error.

* It is not clear if it will ever be possible to remove this syntax entirely.


# Alternatives
[alternatives]: #alternatives

* Status quo.

* Decide on the precise removal plan prior to deprecation.

* Try to solve the underlying annoyances in some other way. For example,
unbounded look ahead can be used in the parser to allow both anonymous
parameters and the full pattern syntax.


# Unresolved questions
[unresolved]: #unresolved-questions

* What deprecation strategy should be chosen?