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

Avoid non-local definitions in functions #3373

Merged
88 changes: 88 additions & 0 deletions text/3373-avoid-nonlocal-definitions-in-fns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
- Feature Name: N/A
- Start Date: 2022-01-19
- RFC PR: [rust-lang/rfcs#3373](https://github.com/rust-lang/rfcs/pull/3373)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Add a warn-by-default lint for items inside functions or expressions that
implement methods or traits that are visible outside the function or
expression. Consider ramping that lint to deny-by-default for Rust 2024, and
evaluating a hard error for 2027.

# Motivation
[motivation]: #motivation

Currently, tools cross-referencing uses and definitions (such as IDEs) must
either search inside all function bodies and other expression-containing items
to find potential definitions corresponding to uses within another function, or
not cross-reference those definitions at all.

Humans cross-referencing such uses and definitions may find themselves
similarly baffled.

This change helps humans limit the scope of their search and avoid looking for
definitions inside other functions or items, without missing any relevant
definitions. If in the future we manage to forbid it entirely within a
subsequent Rust edtion, tools will be able to rely on this as well.

# Explanation
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
[explanation]: #explanation

The following types of items, "expression-containing items", can contain
expressions, including the definitions of other items:
- Functions
- Closures
- The values assigned to `static` items or non-anonymous `const` items.
- The discriminant values assigned to `enum` variants

Rust will emit a warn-by-default lint for all of the following cases:
- An item nested inside an expression-containing item (through any level of
nesting) may not define an `impl Type` block unless the `Type` is also nested
inside the same expression-containing item.
- An item nested inside an expression-containing item (through any level of
nesting) may not define an `impl Trait for Type` unless either the `Trait` or
the `Type` is also nested inside the same expression-containing item.
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
- An item nested inside an expression-containing item (through any level of
nesting) may not define an exported macro visible outside the
expression-containing item (e.g. using `#[macro_export]`).
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved

In a future edition, we may consider making this lint deny-by-default, or
eventually making it a hard error. We'll evaluate the impact on the ecosystem
and existing use cases before doing so.

The lint is considered to attach to the `impl` token of an `impl` block, or the
`macro_rules!` token of a macro definition.

# Drawbacks
[drawbacks]: #drawbacks

Some existing code makes use of this pattern, and would need to migrate to a
different pattern. In particular, this pattern may occur in macro-generated
code, or in code generated by tools like rustdoc. Making this change would
require such code and tools to restructure to meet this requirement.

joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
# Prior art
[prior-art]: #prior-art

Other aspects of Rust's design attempt to enable local reasoning and avoid
global reasoning, including non-inference of function signatures, and not
having the function body affect non-opaque properties of `impl Trait` uses in
the signature without reflecting those properties in the signature.

# Unresolved questions
[unresolved-questions]: #unresolved-questions

We'll need a crater run to look at how widespread this pattern is in existing
code.

Should we flag these definitions in anonymous `const` items as well, or would
that produce unwanted warnings?

# Future possibilities
[future-possibilities]: #future-possibilities

If in the future Rust provides a "standalone `derive`" mechanism (e.g. `derive
Trait for Type` as a standalone definition separate from `Type`), the `impl`
produced by that mechanism would be subject to the same requirements.