From 9f5694bfe984e93e1b9864254e5b384cc588316b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 4 Jun 2021 05:07:50 -0700 Subject: [PATCH] Add "scopes" chapter. --- src/expressions/block-expr.md | 2 + src/items.md | 18 +- src/items/generics.md | 3 + src/names.md | 2 +- src/names/scopes.md | 323 +++++++++++++++++++++++++++++++++- src/patterns.md | 3 +- src/statements.md | 3 +- src/trait-bounds.md | 3 +- 8 files changed, 343 insertions(+), 14 deletions(-) diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index deece94ca..280e8ef32 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -15,6 +15,7 @@ A *block expression*, or *block*, is a control flow expression and anonymous namespace scope for items and variable declarations. As a control flow expression, a block sequentially executes its component non-item declaration statements and then its final optional expression. As an anonymous namespace scope, item declarations are only in scope inside the block itself and variables declared by `let` statements are in scope from the next statement until the end of the block. +See the [scopes] chapter for more details. The syntax for a block is `{`, then any [inner attributes], then any number of [statements], then an optional expression, called the final operand, and finally a `}`. @@ -181,6 +182,7 @@ fn is_unix_platform() -> bool { [inner attributes]: ../attributes.md [method]: ../items/associated-items.md#methods [mutable reference]: ../types/pointer.md#mutables-references- +[scopes]: ../names/scopes.md [shared references]: ../types/pointer.md#shared-references- [statement]: ../statements.md [statements]: ../statements.md diff --git a/src/items.md b/src/items.md index addbe0efd..9bc07a62c 100644 --- a/src/items.md +++ b/src/items.md @@ -53,15 +53,10 @@ There are several kinds of items: * [implementations] * [`extern` blocks] -Some items form an implicit scope for the declaration of sub-items. In other -words, within a function or module, declarations of items can (in many cases) -be mixed with the statements, control blocks, and similar artifacts that -otherwise compose the item body. The meaning of these scoped items is the same -as if the item was declared outside the scope — it is still a static item -— except that the item's *path name* within the module namespace is -qualified by the name of the enclosing item, or is private to the enclosing -item (in the case of functions). The grammar specifies the exact locations in -which sub-item declarations may appear. +Items may be declared in the [root of the crate], a [module][modules], or a [statement]. +Additionally, a subset of items, called [associated items], may be declared in [traits] and [implementations]. + +See [item scopes] for information on the scoping rules of items. [_ConstantItem_]: items/constant-items.md [_Enumeration_]: items/enumerations.md @@ -83,14 +78,19 @@ which sub-item declarations may appear. [`extern crate` declarations]: items/extern-crates.md [`extern` blocks]: items/external-blocks.md [`use` declarations]: items/use-declarations.md +[associated items]: items/associated-items.md [constant items]: items/constant-items.md [enumeration definitions]: items/enumerations.md [function definitions]: items/functions.md [implementations]: items/implementations.md +[item scopes]: names/scopes.md#item-scopes [modules]: items/modules.md [paths]: paths.md +[root of the crate]: crates-and-source-files.md +[statement]: statements.md [static items]: items/static-items.md [struct definitions]: items/structs.md [trait definitions]: items/traits.md +[traits]: items/traits.md [type definitions]: items/type-aliases.md [union definitions]: items/unions.md diff --git a/src/items/generics.md b/src/items/generics.md index 946b5e9a5..ed258d2dd 100644 --- a/src/items/generics.md +++ b/src/items/generics.md @@ -23,6 +23,7 @@ parameters are listed in angle brackets (`<...>`)([T; N]); Generic parameters are in scope within the item definition where they are declared. They are not in scope for items declared within the body of a function as described in [item declarations]. +See [generic parameter scopes] for more details. [References], [raw pointers], [arrays], [slices][arrays], [tuples], and [function pointers] have lifetime or type parameters as well, but are not @@ -296,6 +298,7 @@ struct Foo<#[my_flexible_clone(unbounded)] H> { [functions]: functions.md [function pointers]: ../types/function-pointer.md [generic implementations]: implementations.md#generic-implementations +[generic parameter scopes]: ../names/scopes.md#generic-parameter-scopes [higher-ranked lifetimes]: ../trait-bounds.md#higher-ranked-trait-bounds [implementations]: implementations.md [item declarations]: ../statements.md#item-declarations diff --git a/src/names.md b/src/names.md index fd8f50cd0..46d34459e 100644 --- a/src/names.md +++ b/src/names.md @@ -13,7 +13,7 @@ Some entities are [explicitly declared](#explicitly-declared-entities) in the source code, and some are [implicitly declared](#implicitly-declared-entities) as part of the language or compiler extensions. -[*Paths*] are used to refer to an entity, possibly in another scope. Lifetimes +[*Paths*] are used to refer to an entity, possibly in another module or type. Lifetimes and loop labels use a [dedicated syntax][lifetimes-and-loop-labels] using a leading quote. diff --git a/src/names/scopes.md b/src/names/scopes.md index 288781bd3..066faed51 100644 --- a/src/names/scopes.md +++ b/src/names/scopes.md @@ -1,3 +1,324 @@ # Scopes -> **Note**: This is a placeholder for future expansion. +A *scope* is the region of source text where a named [entity] may be referenced with that name. +The following sections provide details on the scoping rules and behavior, which depend on the kind of entity and where it is declared. +The process of how names are resolved to entities is described in the [name resolution] chapter. +More information on "drop scopes" used for the purpose of running destructors maybe be found in the [destructors] chapter. + +## Item scopes + +The name of an [item][items] declared directly in a [module] has a scope that extends from the start of the module to the end of the module. These items are also members of the module and can be referred to with a [path] leading from their module. + +The name of an item declared as a [statement] has a scope that extends from the start of the block the item statement is in until the end of the block. + +It is an error to introduce an item with a duplicate name of another item in the same [namespace] within the same module or block. +[Asterisk glob imports] have special behavior for dealing with duplicate names and shadowing, see the linked chapter for more details. +Items in a module may shadow items in a [prelude](#prelude-scopes). + +Item names from outer modules are not in scope within a nested module. +A [path] may be used to refer to an item in another module. + +### Associated item scopes + +[Associated items] are not scoped and can only be referred to by using a [path] leading from the type they are associated with. +[Methods] can also be referred to via [call expressions]. + +## Pattern binding scopes + +The scope of a local variable [pattern] binding depends on where it is used: + +* [`let` statement] bindings range from just after the `let` statement until the end of the block where it is declared. +* [Function parameter] bindings are within the body of the function. +* [Closure parameter] bindings are within the closure body. +* [`for`] and [`while let`] bindings are within the loop body. +* [`if let`] bindings are within the consequent block. +* [`match` arms] bindings are within the [match guard] and the match arm expression. + +Local variable scopes do not extend into item declarations. + + +### Pattern binding shadowing + +Pattern bindings are allowed to shadow any name in scope with the following exceptions which are an error: + +* [Const generic parameters] +* [Static items] +* [Const items] +* Constructors for [structs] and [enums] + +The following example illustrates how local bindings can shadow item declarations: + +```rust +fn shadow_example() { + // Since there are no local variables in scope yet, this resolves to the function. + foo(); // prints `function` + let foo = || println!("closure"); + fn foo() { println!("function"); } + // This resolves to the local closure since it shadows the item. + foo(); // prints `closure` +} +``` + +## Generic parameter scopes + +Generic parameters are declared in a [_GenericParams_] list. +The scope of a generic parameter is within the item it is declared on. + +All parameters are in scope within the generic parameter list regardless of the order they are declared. +The following shows some examples where a parameter may be referenced before it is declared: + +```rust +// The 'b bound is referenced before it is declared. +fn params_scope<'a: 'b, 'b>() {} + +# trait SomeTrait {} +// The const N is referenced in the trait bound before it is declared. +fn f, const N: usize>() {} +``` + +Generic parameters are also in scope for type bounds and where clauses, for example: + +```rust +# trait SomeTrait<'a, T> {} +// The <'a, U> for `SomeTrait` refer to the 'a and U parameters of `bounds_scope`. +fn bounds_scope<'a, T: SomeTrait<'a, U>, U>() {} + +fn where_scope<'a, T, U>() + where T: SomeTrait<'a, U> +{} +``` + +Generic scopes do not extend into [items] declared inside a function. + +### Generic parameter shadowing + +It is an error to shadow a generic parameter. +Items declared within functions are allowed to reuse generic parameter names from the function because generic scopes do not extend to inner items. + +```rust +fn example<'a>() { + // Items within functions are allowed to reuse generic parameter in scope + // because all generics are not in scope within inner items. + fn inner<'a>() {} // OK +} +``` + +```rust,compile_fail +trait SomeTrait<'a> { + fn example<'a>() {} // ERROR: 'a is already in scope +} +``` + +### Lifetime scopes + +Lifetime parameters are declared in a [_GenericParams_] list and [higher-ranked trait bounds][hrtb]. + +The `'static` lifetime and [placeholder lifetime] `'_` have a special meaning and cannot be declared as a parameter. + +#### Lifetime generic parameter scopes + +[Constant] and [static] items and [const contexts] only ever allow `'static` lifetime references, so no other lifetime may be in scope within them. +[Associated consts] do allow referring to lifetimes declared in their trait or implementation. + +#### Higher-ranked trait bound scopes + +The scope of a lifetime parameter declared as a [higher-ranked trait bound][hrtb] depends on the scenario where it is used. + +* As a [_TypeBoundWhereClauseItem_] the declared lifetimes are in scope in the type and the type bounds. +* As a [_TraitBound_] the declared lifetimes are in scope within the bound type path. +* As a [_BareFunctionType_] the declared lifetimes are in scope within the function parameters and return type. + +```rust +# trait Trait<'a>{} + +fn where_clause() + // 'a is in scope in both the type and the type bounds. + where for <'a> &'a T: Trait<'a> +{} + +fn bound() + // 'a is in scope within the bound. + where T: for <'a> Trait<'a> +{} + +# struct Example<'a> { +# field: &'a u32 +# } + +// 'a is in scope in both the parameters and return type. +type FnExample = for<'a> fn(x: Example<'a>) -> Example<'a>; +``` + +#### Impl trait restrictions + +[Impl trait] types can only reference lifetimes declared on a function or implementation. + + +```rust +# trait Trait1 { +# type Item; +# } +# trait Trait2<'a> {} +# +# struct Example; +# +# impl Trait1 for Example { +# type Item = Element; +# } +# +# struct Element; +# impl<'a> Trait2<'a> for Element {} +# +// The `impl Trait2` here is not allowed to refer to 'b but it is allowed to +// refer to 'a. +fn foo<'a>() -> impl for<'b> Trait1> { + // ... +# Example +} +``` + +## Loop label scopes + +[Loop labels] may be declared by a [loop expression]. +The scope of a loop label is from the point it is declared till the end of the loop expression. +The scope does not extend into [items], [closures], [async blocks], [const arguments], [const contexts], and the iterator expression of the defining [`for` loop]. + +```rust +'a: for n in 0..3 { + if n % 2 == 0 { + break 'a; + } + fn inner() { + // Using 'a here would be an error. + // break 'a; + } +} + +// The label is in scope for the expression of `while` loops. +'a: while break 'a {} // Loop does not run. +'a: while let _ = break 'a {} // Loop does not run. + +// The label is not in scope in the defining `for` loop: +'a: for outer in 0..5 { + // This will break the outer loop, skipping the inner loop and stopping + // the outer loop. + 'a: for inner in { break 'a; 0..1 } { + println!("{}", inner); // This does not run. + } + println!("{}", outer); // This does not run, either. +} + +``` + +Loop labels may shadow labels of the same name in outer scopes. +References to a label refer to the closest definition. + +```rust +// Loop label shadowing example. +'a: for outer in 0..5 { + 'a: for inner in 0..5 { + // This terminates the inner loop, but the outer loop continues to run. + break 'a; + } +} +``` + +> **Note**: `rustc` currently warns about defining the same loop label multiple times in the same function, or if the label has the same name as an in-scope lifetime. +> This is intended as a future-compatibility warning about a possible extension to the language. +> See [PR #24162](https://github.com/rust-lang/rust/pull/24162). + +## Prelude scopes + +[Preludes] bring entities into scope of every module. +The preludes are layered such that one shadows another if they contain entities of the same name. +The order that preludes may shadow other preludes is the following where earlier entries may shadow later ones: + +1. [Extern prelude] +2. [Tool prelude] +3. [`macro_use` prelude] +4. [Standard library prelude] +5. [Language prelude] + +## `macro_rules` scopes + +The scope of `macro_rules` macros is described in the [Macros By Example] chapter. +The behavior depends on the use of the [`macro_use`] and [`macro_export`] attributes. + +## Derive macro helper attributes + +[Derive macro helper attributes] are in scope in the item where their corresponding [`derive` attribute] is specified. +The scope extends from just after the `derive` attribute to the end of the item. +Helper attributes shadow other attributes of the same name in scope. + +## `Self` scope + +Although [`Self`] is a keyword with special meaning, it interacts with name resolution in a way similar to normal names. + +The implicit `Self` type in the definition of a [struct], [enum], [union], [trait], or [implementation] is treated similarly to a [generic parameter](#generic-parameter-scopes), and is in scope in the same way as a generic type parameter. +For struct, enum, and unions, it is in scope starting after the generic parameters. +For traits and implementations, it is in scope starting just before the generic parameters. + +The implicit `Self` constructor in the value [namespace] of an [implementation] is in scope within the body of the implementation (the implementation's [associated items]). + +[_BareFunctionType_]: ../types/function-pointer.md +[_GenericParams_]: ../items/generics.md +[_TraitBound_]: ../trait-bounds.md +[_TypeBoundWhereClauseItem_]: ../items/generics.md +[`derive` attribute]: ../attributes/derive.md +[`for` loop]: ../expressions/loop-expr.md#iterator-loops +[`for`]: ../expressions/loop-expr.md#iterator-loops +[`if let`]: ../expressions/if-expr.md#if-let-expressions +[`let` statement]: ../statements.md#let-statements +[`macro_export`]: ../macros-by-example.md#path-based-scope +[`macro_use` prelude]: preludes.md#macro_use-prelude +[`macro_use`]: ../macros-by-example.md#the-macro_use-attribute +[`match` arms]: ../expressions/match-expr.md +[`Self`]: ../paths.md#self-1 +[`while let`]: ../expressions/loop-expr.md#predicate-pattern-loops +[Associated consts]: ../items/associated-items.md#associated-constants +[associated items]: ../items/associated-items.md +[Asterisk glob imports]: ../items/use-declarations.md +[async blocks]: ../expressions/block-expr.md#async-blocks +[call expressions]: ../expressions/call-expr.md +[Closure parameter]: ../expressions/closure-expr.md +[closures]: ../expressions/closure-expr.md +[const arguments]: ../items/generics.md#const-generics +[const contexts]: ../const_eval.md#const-context +[Const generic parameters]: ../items/generics.md#const-generics +[Const items]: ../items/constant-items.md +[Constant]: ../items/constant-items.md +[Derive macro helper attributes]: ../procedural-macros.md#derive-macro-helper-attributes +[destructors]: ../destructors.md +[entity]: ../names.md +[enum]: ../items/enumerations.mdr +[enums]: ../items/enumerations.md +[Extern prelude]: preludes.md#extern-prelude +[Function parameter]: ../items/functions.md#function-parameters +[hrtb]: ../trait-bounds.md#higher-ranked-trait-bounds +[Impl trait]: ../types/impl-trait.md +[implementation]: ../items/implementations.md +[items]: ../items.md +[Language prelude]: preludes.md#language-prelude +[loop expression]: ../expressions/loop-expr.md +[Loop labels]: ../expressions/loop-expr.md#loop-labels +[Macros By Example]: ../macros-by-example.md +[match guard]: ../expressions/match-expr.md#match-guards +[methods]: ../items/associated-items.md#methods +[module]: ../items/modules.md +[name resolution]: name-resolution.md +[namespace]: namespaces.md +[path]: ../paths.md +[pattern]: ../patterns.md +[placeholder lifetime]: ../lifetime-elision.md +[preludes]: preludes.md +[Standard library prelude]: preludes.md#standard-library-prelude +[statement]: ../statements.md +[Static items]: ../items/static-items.md +[static]: ../items/static-items.md +[struct]: ../items/structs.md +[structs]: ../items/structs.md +[Tool prelude]: preludes.md#tool-prelude +[trait]: ../items/traits.md +[union]: ../items/unions.md diff --git a/src/patterns.md b/src/patterns.md index 121b825d1..a2baa99d4 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -176,7 +176,7 @@ for i in -2..5 { Identifier patterns bind the value they match to a variable. The identifier must be unique within the pattern. The variable will shadow any variables of -the same name in scope. The scope of the new binding depends on the context of +the same name in scope. The [scope] of the new binding depends on the context of where the pattern is used (such as a `let` binding or a `match` arm). Patterns that consist of only an identifier, possibly with a `mut`, match any value and @@ -779,6 +779,7 @@ refer to refutable constants or enum variants for enums with multiple variants. [IDENTIFIER]: identifiers.md [enums]: items/enumerations.md [literals]: expressions/literal-expr.md +[scope]: names/scopes.md [structs]: items/structs.md [tuples]: types/tuple.md [scrutinee]: glossary.md#scrutinee diff --git a/src/statements.md b/src/statements.md index 5d8e0e95d..1dda306df 100644 --- a/src/statements.md +++ b/src/statements.md @@ -29,7 +29,7 @@ statements. An *item declaration statement* has a syntactic form identical to an [item declaration][item] within a [module]. Declaring an item within a statement -block restricts its scope to the block containing the statement. The item is not +block restricts its [scope] to the block containing the statement. The item is not given a [canonical path] nor are any sub-items it may declare. The exception to this is that associated items defined by [implementations] are still accessible in outer scopes as long as the item and, if applicable, trait are accessible. @@ -127,6 +127,7 @@ statement are [`cfg`], and [the lint check attributes]. [`cfg`]: conditional-compilation.md [the lint check attributes]: attributes/diagnostics.md#lint-check-attributes [pattern]: patterns.md +[scope]: names/scopes.md [_ExpressionStatement_]: #expression-statements [_Expression_]: expressions.md [_Item_]: items.md diff --git a/src/trait-bounds.md b/src/trait-bounds.md index fe677a9a1..b5a6b88e6 100644 --- a/src/trait-bounds.md +++ b/src/trait-bounds.md @@ -125,7 +125,7 @@ fn call_on_ref_zero(f: F) where for<'a> F: Fn(&'a i32) { ``` Higher-ranked lifetimes may also be specified just before the trait, the only -difference is the scope of the lifetime parameter, which extends only to the +difference is the [scope][hrtb-scopes] of the lifetime parameter, which extends only to the end of the following trait instead of the whole bound. This function is equivalent to the last one. @@ -141,6 +141,7 @@ fn call_on_ref_zero(f: F) where F: for<'a> Fn(&'a i32) { [`Sized`]: special-types-and-traits.md#sized [associated types]: items/associated-items.md#associated-types +[hrtb-scopes]: names/scopes.md#higher-ranked-trait-bound-scopes [supertraits]: items/traits.md#supertraits [generic]: items/generics.md [Trait]: items/traits.md#trait-bounds