Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-502 · Case file with fixtures · Case 474 of 694 · Compiler evidence

A Rust Struct Literal Cannot Set the Same Field Twice

Each field in a Rust struct expression has one owner and one initializer. Duplicate fields usually expose a merge, macro, or policy conflict that should be resolved deliberately rather than by arbitrary deletion.

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
Struct construction has one initializer per field rather than map-like overwrite semantics, leaving the duplicate values as an unresolved policy conflict.
First discriminating check
Trace each candidate value and any shorthand or macro expansion, resolve precedence before construction, and emit the final field exactly once.

I constructed Limits with retries: 2 and later retries: 4 in the same literal. Rust emitted E0062 because one field cannot receive two initializers.

The failing fixture looks like a simple duplicate line. In real configuration code, the two values often come from different assumptions: a default, an environment override, or two merged branches.

A struct expression creates one complete value

The Reference on struct expressions defines named field initializers within one construction expression. Each field position in the resulting value can contain one value, so its name appears at most once.

Rust does not treat later fields like map entries that overwrite earlier fields. Silent last-write-wins behaviour would evaluate the first expression for no stored result and could hide policy errors.

The official E0062 page therefore asks for each field exactly once.

The repair must choose the intended value

The repaired fixture keeps retries: 4 and verifies the completed value. That choice is obvious only because this fixture defines the expected outcome.

In production, I trace both candidates. One may be a safe default and the other a validated override. I then model the precedence before the literal, for example by computing let retries = configured.unwrap_or(DEFAULT_RETRIES).

The final struct expression should receive the already decided value once.

Duplicate fields often reveal merge conflicts

Two branches can independently add the same field with different values. A textual merge may preserve both because the lines are separated by other fields.

Deleting either line based on position is risky. I compare tests, configuration documentation, and the call site's role. If both policies are valid in different environments, they need an explicit conditional before construction rather than duplicate syntax.

The compiler identifies the conflict early, before one branch silently wins.

Macros should deduplicate by field identity

A macro may gather defaults, user overrides, and derived fields into a token list. If it prints all sources directly, duplicate names cause E0062.

I make the generator resolve precedence in its own data model, then emit one initializer per field. This also lets it give a domain-specific error when two user attributes conflict.

Compile tests should include the combination that previously produced duplicates, because testing each feature separately will miss the interaction.

Functional update syntax is not duplicate override syntax

Config { retries: 4, ..base } can explicitly replace a field while moving or copying remaining fields from a base struct. The named field appears once in the new literal; the base supplies only fields not explicitly provided.

This is appropriate when I already have a complete value of the same struct type. It is not available for every enum variant, and privacy or move behaviour can constrain it.

I use it to express controlled update, not to avoid deciding configuration precedence.

Side effects make silent overwrite especially dangerous

Imagine the first initializer calls a function that allocates an ID or records a metric. If a language silently accepted a later duplicate, that effect might still happen even though its value is discarded.

Rust's rejection keeps evaluation and ownership clear. I compute conditional effects in ordinary control flow where their execution is visible, then construct the result once.

This is one small way struct literals remain declarative rather than becoming imperative update lists.

Field shorthand can hide one of the copies

Rust lets retries stand for retries: retries when a local has the same name. A literal containing both retries shorthand and retries: configured still duplicates the field even though the lines look different. I expand shorthand mentally when reviewing E0062.

This also happens after a refactor replaces an explicit expression with a local but leaves the original field below. Searching for retries: alone can miss the shorthand entry, so I inspect the complete literal and any macro expansion rather than relying on a textual colon search.

My E0062 checklist

  • Which field name appears more than once?
  • What policy or branch produced each candidate value?
  • Is this a merge conflict rather than a typo?
  • Should precedence be computed before construction?
  • Did a macro combine defaults and overrides without deduplication?
  • Would functional update syntax express a real base-plus-override operation?
  • Do either initializer expressions have side effects?
  • Does the repaired test assert the chosen semantics?

The core principle is that a struct field receives one value during construction. When two sources compete, I resolve their policy explicitly and pass only the final decision into the literal.