RFA-472 · Case file with fixtures · Case 444 of 694 · Compiler evidence
A Rust Import Cannot Redefine a Local Item Name
Local declarations and imports share namespace scope. Keep the local item, alias the imported operation, or call it through its module path; do not rely on declaration order or function signatures to disambiguate.
- Reviewed
- Rust
- Rust 1.98.1, edition 2024
- Targets
- all Rust targets
- Profiles
- dev, release, test
Direct answer
What this Rust failure means
- Why it happens
- A use alias and a directly declared module item participate in the same namespace without declaration-order shadowing or signature overloading.
- First discriminating check
- Decide which name owns the public or orchestration role, then qualify or semantically alias the helper and review whether the local wrapper still adds policy.
I imported helpers::run into a module that already declares its own fn run. Rust emitted E0255 because the imported alias and local item both claim run in the value namespace.
The failing fixture differs from two-import E0252: one side of this collision is defined directly in the current module.
Imports and declarations meet in one scope
The use-declaration Reference describes use as creating local aliases. Those aliases do not receive a lower priority than functions declared in the same module.
Rust cannot decide whether run() means the local orchestration function or the imported helper. Source order does not create shadowing between these module items.
The E0255 explanation offers aliasing or parent-path qualification as repairs.
Alias the imported operation by its role
The repaired fixture uses:
use helpers::run as run_helper;
In production code, I prefer a semantic alias such as run_probe, run_query, or run_cleanup. The local run often represents orchestration, while the imported function performs one step.
This naming makes the call graph easier to scan and avoids accidental recursion when someone expects one run to call the other.
Keep the module path when it carries context
Calling helpers::run() can be clearer than introducing another local verb. I usually keep qualification when the operation appears only once or twice.
For frequent calls, a concise module alias such as use long_dependency::helpers as helper; preserves context without repetitive paths.
The aim is not to minimise characters; it is to minimise ambiguity in the reader's mental model.
Local shadowing is different inside blocks
Local variable bindings may shadow earlier bindings in nested or sequential scopes. That familiar rule does not mean module item imports can silently replace same-named declarations.
The scope Reference describes the regions where names are valid. I identify whether the collision involves module items, imports, or block-local values before applying a shadowing intuition.
Moving an import into a function block can change scope, but it should not be used to hide unclear architecture.
Check whether the local item duplicates the helper
Sometimes a local wrapper and imported function have converged after refactoring. Before keeping both with aliases, I compare their error mapping, tracing, defaults, and visibility.
If the wrapper adds no contract, removing it can simplify the API. If it adds policy, its role deserves a policy-oriented name rather than the same raw operation name.
Repository history often explains which layer should remain stable.
Function signatures do not resolve the name
One function may take no arguments and another may take a context, but Rust still rejects the duplicated local name. Name resolution selects an item before ordinary call type checking.
If several types implement one conceptual action, trait methods or associated functions may provide qualified identity. Otherwise different operation names are straightforward.
I do not simulate overloading through complex generic dispatch unless the abstraction is real.
Macros can generate the local declaration
An imported function may seem unopposed in source while a macro expansion emits a same-named helper. I inspect expansion and search generated modules when E0255 points to a location that does not show both declarations plainly.
Generators should prefix private helpers or contain them in a submodule to avoid consuming common caller names.
My E0255 checklist
In public modules, I also inspect documentation links and doctests before renaming the local item. A local run may be an established entry point while the helper import is private implementation detail, making the helper alias the low-cost repair. The opposite can be true during a façade migration. I treat name ownership as part of API history: which path do users know, which one appears in examples, and which operation is intended to remain stable?
This prevents a local compile fix from causing avoidable downstream churn.
- Which item is declared directly in this module?
- Which import creates the competing local alias?
- Is the imported function better called through its module path?
- What semantic role should an alias communicate?
- Does the local wrapper still add policy or observability?
- Am I incorrectly expecting signature-based overloading?
- Is a macro generating the local item?
- Would changing scope only conceal an unclear boundary?
The core principle is that use participates in the module's real namespace. I preserve one clear identity per name and make orchestration, adaptation, and low-level operations distinct through paths or vocabulary.