🔍

Names and Scopes

Identifiers, Namespaces, and Name Resolution

🏷️ Names and Scopes Quick Reference

Identifier
Names for entities in Rust code
snake_case - Variables, functions
CamelCase - Types, traits
SCREAMING_SNAKE - Constants
Scope
Where names are valid
{ } - Block scope
fn - Function scope
Lexical scoping rules
Namespace
Separate name spaces
Value, Type, Macro, Lifetime
Same name, different namespace
Visibility
Control access to names
pub - Public
pub(crate) - Crate-only
pub(super) - Parent module

Identifier Grammar

IDENTIFIER START_CHAR CONTINUE_CHAR*
START_CHAR
LETTER | _ | UNICODE
CONTINUE_CHAR
LETTER | DIGIT | _

Name Resolution Grammar

NAME := IDENTIFIER | PATH PATH := ABSOLUTE_PATH | RELATIVE_PATH ABSOLUTE_PATH := "crate::" SEGMENTS RELATIVE_PATH := ("super::" | "self::")? SEGMENTS SEGMENTS := IDENTIFIER ("::" IDENTIFIER)* VISIBILITY := "pub" | "pub(crate)" | "pub(super)" | "pub(in" PATH ")"

Actual Rust Usage

// Identifiers
let snake_case_var = 42;
struct CamelCaseType;
const SCREAMING_SNAKE: i32 = 100;

// Paths
use crate::module::item;      // Absolute
use super::sibling::thing;    // Relative
use self::child::helper;      // Self-relative

// Visibility
pub fn public_function() {}
pub(crate) fn crate_function() {}

🎮 Name Resolution Explorer

Names and scopes form the foundation of Rust's namespace system. Understanding how Rust resolves names, manages visibility, and organizes code into hierarchical namespaces is essential for writing well-structured Rust programs.

Rust's naming system provides fine-grained control over visibility and organization while maintaining memory safety and preventing name conflicts through its sophisticated module system.

Identifiers

Identifiers are names used to refer to various entities in Rust code, including variables, functions, types, modules, and other items.

// Valid identifiers
let variable_name = 42;
let _private = "hidden";
let CamelCase = true;
let snake_case_function = || {};

// Unicode identifiers are allowed
let 变量 = "Chinese variable name";
let π = 3.14159;

// Raw identifiers (escape keywords)
let r#match = "using match as identifier";
let r#type = "using type as identifier";

// Lifetime identifiers
fn function_with_lifetime<'a>(param: &'a str) -> &'a str {
    param
}

// Type parameter identifiers
struct GenericStruct {
    field1: T,
    field2: U,
}

Identifier Rules

  • Start: Letter, underscore, or Unicode character
  • Continue: Letters, digits, underscores, or Unicode
  • Keywords: Cannot use reserved keywords (unless raw identifiers)
  • Case Sensitive: Variable and variable are different

Identifier Pattern Matching

Pattern r#{keyword} Escaped keyword as identifier
Example let r#match = 5; Use 'match' keyword as variable name

Scopes and Lexical Structure

Scopes define where names are valid and accessible. Rust uses lexical scoping, meaning the scope of a name is determined by where it appears in the source code.

{ // Outer scope let outer_var = 10; { // Inner scope let inner_var = 20; // Both outer_var and inner_var accessible here println!("{} {}", outer_var, inner_var); let outer_var = 30; // Shadows outer outer_var // outer_var now refers to 30 in this scope } // inner_var goes out of scope here // Only outer_var (= 10) accessible here // inner_var is no longer valid } // outer_var goes out of scope here

Variable Shadowing

fn demonstrate_shadowing() {
    let x = 5;
    println!("x = {}", x);  // x = 5
    
    let x = x + 1;  // Shadows previous x
    println!("x = {}", x);  // x = 6
    
    {
        let x = x * 2;  // Shadows in inner scope
        println!("x = {}", x);  // x = 12
    }
    
    println!("x = {}", x);  // x = 6 (outer shadow)
    
    // Can change type when shadowing
    let x = "Hello";  // Now x is a string
    println!("x = {}", x);  // x = Hello
}

Namespaces

Rust organizes names into several distinct namespaces. Items in different namespaces can have the same name without conflict.

Rust Namespaces

  • Value namespace: Functions, variables, constants
  • Type namespace: Types, type aliases, trait names
  • Macro namespace: Macros and procedural macros
  • Lifetime namespace: Lifetime parameters
  • Label namespace: Loop and block labels
// Different namespaces can have same names
struct Option;           // Type namespace
const Option: i32 = 42; // Value namespace
macro_rules! Option {    // Macro namespace
    () => { "macro" };
}

fn example<'Option>() {  // Lifetime namespace
    let value = Option;           // Refers to constant
    let instance: Option = Option; // Type and constant
    let result = Option!();       // Macro
    
    // Label namespace
    'Option: loop {
        break 'Option;
    }
}

Module System and Visibility

Rust's module system provides hierarchical namespaces with fine-grained visibility control.

// Module definitions and visibility
mod outer_module {
    // Private by default
    fn private_function() {
        println!("Private function");
    }
    
    // Explicitly public
    pub fn public_function() {
        println!("Public function");
        private_function();  // Can access private items in same module
    }
    
    pub mod inner_module {
        pub fn inner_public() {
            // Can access parent's private items
            super::private_function();
        }
        
        // Visible only to parent module
        pub(super) fn visible_to_parent() {
            println!("Visible to parent");
        }
        
        // Visible within entire crate
        pub(crate) fn crate_visible() {
            println!("Visible to entire crate");
        }
        
        // Visible only in this module and descendants
        pub(in crate::outer_module) fn restricted_visibility() {
            println!("Restricted visibility");
        }
    }
}

// Usage
fn main() {
    outer_module::public_function();
    outer_module::inner_module::inner_public();
    
    // These would be compilation errors:
    // outer_module::private_function();  // Private
    // outer_module::inner_module::visible_to_parent();  // Not visible here
}

Name Resolution

Rust follows specific rules for name resolution - determining what entity a name refers to in a given context.

Resolution Order

  1. Built-in types: Primitive types like i32, bool
  2. Local scope: Variables and parameters in current scope
  3. Item scope: Items in current module
  4. Prelude: Standard library prelude items
  5. Extern crate: External crate root modules
use std::collections::HashMap;

// Module-level items
const GLOBAL_CONSTANT: i32 = 100;

fn name_resolution_example() {
    let local_var = 42;
    
    // Refers to local variable
    println!("{}", local_var);
    
    // Refers to module-level constant
    println!("{}", GLOBAL_CONSTANT);
    
    // HashMap from std::collections via use statement
    let mut map = HashMap::new();
    map.insert("key", "value");
    
    // Fully qualified syntax
    let vec = std::vec::Vec::::new();
    
    // Absolute path from crate root
    let result = crate::name_resolution_example;
}

// Disambiguation with fully qualified syntax
trait A {
    fn method(&self);
}

trait B {
    fn method(&self);
}

struct S;

impl A for S {
    fn method(&self) {
        println!("Method from trait A");
    }
}

impl B for S {
    fn method(&self) {
        println!("Method from trait B");
    }
}

fn disambiguation() {
    let s = S;
    
    // Ambiguous - compilation error
    // s.method();
    
    // Disambiguate using fully qualified syntax
    A::method(&s);
    B::method(&s);
    
    // Alternative syntax
    ::method(&s);
    ::method(&s);
}

Use Statements and Imports

The use statement brings names from other modules into scope:

// Various forms of use statements
use std::collections::HashMap;                    // Single import
use std::collections::{HashSet, BTreeMap};       // Multiple imports
use std::collections::*;                         // Glob import (discouraged)
use std::io::Result as IoResult;                 // Rename import
use std::fs::{self, File};                       // Import module and item

// Re-export for external users
pub use std::collections::HashMap as Map;

// Conditional imports
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;

#[cfg(windows)]
use std::os::windows::fs::MetadataExt;

// Use in different scopes
fn scoped_use() {
    use std::thread;
    
    thread::spawn(|| {
        println!("Thread work");
    });
    
    // thread is only available in this function
}

// Importing from current crate
mod my_module {
    pub fn helper() {}
}

use crate::my_module::helper;  // Absolute path
use self::my_module::helper as my_helper;  // Relative path

Paths and Path Resolution

Paths specify the location of items within the module hierarchy:

// Different types of paths
mod parent {
    pub mod child {
        pub fn function() {}
        
        pub mod grandchild {
            pub fn deeply_nested() {
                // Absolute path from crate root
                crate::parent::child::function();
                
                // Relative path to parent
                super::function();
                
                // Relative path to grandparent
                super::super::child::function();
                
                // Self reference
                self::deeply_nested();
            }
        }
    }
}

// External crate paths
use ::std::collections::HashMap;  // Absolute from extern crate

// Path segments
// crate::module::submodule::item
// ::extern_crate::module::item
// super::sibling_module::item
// self::child_module::item

Advanced Naming Concepts

Associated Items

struct MyStruct;

impl MyStruct {
    // Associated function (static)
    fn new() -> Self {
        MyStruct
    }
    
    // Associated constant
    const CONSTANT: i32 = 42;
    
    // Method (has self)
    fn method(&self) {
        println!("Method called");
    }
}

// Accessing associated items
fn main() {
    let instance = MyStruct::new();
    instance.method();
    
    println!("Constant: {}", MyStruct::CONSTANT);
    
    // Fully qualified method call
    MyStruct::method(&instance);
}

Trait Items and Disambiguation

trait Display {
    fn display(&self);
}

trait Debug {
    fn display(&self);  // Same name as Display::display
}

struct Item;

impl Display for Item {
    fn display(&self) {
        println!("Display implementation");
    }
}

impl Debug for Item {
    fn display(&self) {
        println!("Debug implementation");
    }
}

fn disambiguation_example() {
    let item = Item;
    
    // Ambiguous without qualification
    // item.display();  // Error: multiple applicable items
    
    // Disambiguate using trait syntax
    Display::display(&item);
    Debug::display(&item);
    
    // Fully qualified syntax
    ::display(&item);
    ::display(&item);
}

🦀 Rust-Specific Naming Quirks

🔗 Raw Identifier Edge Cases

RAW_IDENTIFIER := "r#" IDENTIFIER RESERVED_WORD := "match" | "type" | "async" | "await" USAGE := RAW_IDENTIFIER | IDENTIFIER
// Raw identifiers for keywords
let r#match = "using match as identifier";
let r#type = MyStruct; 
let r#async = true;

// Can't use r# with existing identifiers
// let r#variable = 5; // Error: not needed

🔗 Underscore Identifier Rules

UNDERSCORE_PATTERN := "_" | "_" IDENTIFIER USAGE_RULE := "unused_warning" | "pattern_matching" NAMING_CONVENTION := "_" + descriptive_name
// Underscore patterns
let _ = expensive_computation(); // Discard result
let _unused_var = 42; // Suppress unused warning
let (_first, _) = tuple; // Partial destructuring

// Invalid underscore usage
// let _ = 5; let _ = 10; // OK: _ is special
// println!("{}", _); // Error: _ not bindable

Best Practices

Naming Conventions

  • snake_case: Variables, functions, modules
  • SCREAMING_SNAKE_CASE: Constants, static variables
  • CamelCase: Types, traits, enum variants
  • Descriptive: Use clear, meaningful names

Visibility Guidelines

  • Start with private and expose as needed
  • Use pub(crate) for internal APIs
  • Document public interfaces thoroughly
  • Prefer smaller, focused modules

🤖 For AI Coding Agents

Name Resolution System Instructions

When analyzing names and scopes, follow these machine-readable specifications:

{
  "name_resolution": {
    "version": "1.0",
    "namespaces": {
      "value": {
        "contains": ["functions", "variables", "constants", "statics"],
        "resolution_order": ["local_scope", "item_scope", "prelude", "extern_crate"]
      },
      "type": {
        "contains": ["structs", "enums", "traits", "type_aliases"],
        "conflicts": "separate_from_value_namespace"
      },
      "macro": {
        "contains": ["declarative_macros", "procedural_macros"],
        "expansion_phase": "before_name_resolution"
      },
      "lifetime": {
        "contains": ["lifetime_parameters"],
        "scope": "function_or_impl_block"
      },
      "label": {
        "contains": ["loop_labels", "block_labels"],
        "scope": "local_to_function"
      }
    },
    "visibility_modifiers": {
      "private": "default - accessible only within current module",
      "pub": "public to all external modules",
      "pub(crate)": "visible throughout current crate only",
      "pub(super)": "visible to parent module only",
      "pub(in path)": "visible to specified module path only"
    },
    "path_resolution": {
      "absolute": "crate::path::to::item",
      "relative_parent": "super::sibling::item",
      "relative_self": "self::child::item",
      "extern_crate": "::external_crate::item"
    }
  }
}

Scope Analysis Templates

Scope Entry { scope_level++; track_names(current_scope); } Scope[2]: [outer_var, inner_var] → shadows: [outer_var]
{
  "scope_tracking": {
    "entry_patterns": ["{", "fn", "loop", "if", "match"],
    "exit_patterns": ["}"],
    "shadowing_rules": {
      "allowed": "same_scope_level_or_inner",
      "type_change": "permitted_with_let_rebinding",
      "resolution": "innermost_scope_wins"
    },
    "name_lookup": {
      "order": ["local_bindings", "function_parameters", "item_definitions", "use_imports", "prelude"],
      "disambiguation": "fully_qualified_syntax_required_for_conflicts"
    }
  }
}

✅ Correct: Proper Name Resolution

// ✅ VALID: Clear scope hierarchy
fn proper_scoping() {
    let outer = 10;        // Scope level 0
    
    {                      // Scope level 1 begins
        let inner = 20;    // New binding in inner scope
        let outer = 30;    // Shadows outer, valid
        
        println!("{} {}", outer, inner); // 30, 20
    }                      // Scope level 1 ends
    
    println!("{}", outer); // 10 (original outer)
    // inner not accessible here - good scoping
}

// ✅ VALID: Module visibility control
mod parent {
    pub fn public_function() {}
    
    fn private_function() {}
    
    pub mod child {
        pub fn access_parent() {
            super::private_function(); // Valid: child can access parent private
        }
    }
}

pub use parent::child::access_parent; // Valid re-export

❌ Incorrect: Name Resolution Errors

// ❌ INVALID: Use after scope ends
fn scope_error() {
    {
        let scoped_var = 42;
    }
    println!("{}", scoped_var); // ERROR: not in scope
}

// ❌ INVALID: Ambiguous trait method calls
trait TraitA {
    fn method(&self);
}
trait TraitB {  
    fn method(&self);
}

struct S;
impl TraitA for S { fn method(&self) {} }
impl TraitB for S { fn method(&self) {} }

fn ambiguous_call() {
    let s = S;
    s.method(); // ERROR: ambiguous
    
    // ✅ SOLUTION: Disambiguate
    TraitA::method(&s);
    ::method(&s);
}

Namespace Separation Demo

Value Namespace
const Item: i32 = 42;
Type Namespace
struct Item { field: i32 }
Macro Namespace
macro_rules! Item { ... }
← Conditional Compilation Notation → ← Back to Reference