proposal: spec: Mixed Declaration-Assignment Operators in Multi-Variable Assignments #71522
Closed
3 of 4 tasks
Labels
LanguageChange
Suggested changes to the Go language
LanguageChangeReview
Discussed by language change review committee
LanguageProposal
Issues describing a requested change to the Go language specification.
Proposal
Milestone
Go Programming Experience
Experienced
Other Languages Experience
Go, PHP, Javascript, Bash, SQL
Related Idea
Has this idea, or one like it, been proposed before?
Yes, this concept has been explored in multiple previous Go issue discussions, though with different proposed solutions.
This proposal builds on these earlier discussions while taking a more focused, backward-compatible approach that specifically addresses the refactoring use case. Previous proposals either suggested more sweeping language changes or focused primarily on error handling patterns, whereas this proposal targets a precise pain point with minimal language modification.
Issue #377: Various changes to :=
This early discussion focused primarily on variable shadowing concerns and := behavior with return parameters. While it was ultimately declined ("
:=
is for new variables"), this reasoning actually supports this proposal because::=
declares new variablesIssue #6842: Allow tuple assignment to mix new and existing variables
This issue suggested a different syntax (
x, :=y = expr
) for mixing declarations and assignments. While it was not accepted, this proposal differs in key ways:Note this proposal provides a superior solution to the concerns raised in #6842 about struct field
and array index assignments. Where #6842 raised valid concerns about ambiguous behavior during
error cases, our proposal eliminates this ambiguity through explicit declaration intent:
This explicit syntax addresses the fundamental safety concern raised in #6842 - the risk of unintended modifications during error cases. Rather than restricting functionality, our proposal makes the developer's intentions clear through syntax.
Issue #30318: proposal: spec: make it easier to use existing names in short variable declarations
This discussion provides strong support for this proposal by documenting real-world refactoring pain points:
Issue #31064: proposal: Go 2: short variable declarations with existing variables
This issue further reinforces the need for better handling of existing variables during refactoring, though it proposed a different solution. The discussion supports this proposal's approach because:
Issue #38388: Go 2: reform the variable redeclaration syntax to avoid some confusions and inconveniences
While other proposals like #38388 suggest more sweeping changes to Go's declaration syntax, this
proposal:
This focused approach aligns with Go's philosophy of incremental improvement while solving real developer needs.
Many of the discussions on these issues occurred during consideration of a backward-compatibility-breaking Go 2.0. Since then, the Go team has committed to maintaining backward compatibility, making incremental improvements that don't break existing code. This proposal aligns with that approach by introducing new syntax that's fully backward compatible while addressing long-standing developer pain points.
Does this affect error handling?
This proposal is not focused on error handling; it addresses the broader issue of mixed declaration and assignment operators in multi-variable assignments. However, it would have a positive impact on code organization in error handling scenarios, which represent a common use case for multi-variable assignments in Go.
In current Go code, developers frequently need to predeclare error variables when refactoring error handling logic, particularly when moving error handling code between functions or restructuring error checks. As noted by Robert Griesemer in Issue #6842, this creates situations where developers must add explicit variable declarations, even though the intent and logic of the code remain unchanged.
For example, when refactoring code that handles errors across multiple operations, developers currently write:
The proposed syntax would enable more direct expression of the same logic:
This improvement in code organization is a side benefit of the proposal's broader goal of making variable declaration status more explicit in multi-variable assignments. It maintains Go's existing error handling patterns while reducing the structural overhead commonly encountered when refactoring error handling code. The proposal does not modify how errors are handled or processed; it simply makes the code structure more maintainable when working with existing error handling patterns.
Is this about generics?
While this proposal does not specifically address generics, it has been designed to work seamlessly with Go's generic type system. The proposed syntax for mixed declaration-assignment operators maintains compatibility with generic type inference and type parameter substitution. Since the proposal only affects how variables are declared and assigned, not how types are determined, it integrates naturally with the existing generic type system without requiring any special handling.
Proposal
Abstract
This proposal introduces syntax for allowing mixed declaration (
:=
) and assignment (=
) operators in multi-variable assignments. This enhancement specifically targets reducing cognitive load and boilerplate during code refactoring, a common pain point in Go development.While the current syntax is generally adequate for initial code authoring, refactoring operations
often require developers to add explicit variable declarations and split assignments, increasing complexity and the potential for errors.
This proposal also endures to maintain complete backward compatibility while reducing cognitive
load during common refactoring operations, providing immediate value to Go developers without
requiring additional changes to existing codebases to gain its benefits.
Background
In current Go, when refactoring code with multi-variable assignments, all variables must either be declared using
:=
or assigned using=
. When refactoring moves some variable declarations earlier in the scope but not others, developers must add explicitvar
declarations and change:=
to=
. This creates friction during refactoring and increases the cognitive load of what should be simple code modifications.Many developers note that while writing new code with the current rules are straightforward _
— e.g. "just declare the variable" — the real pain point emerges during refactoring when code
structure needs to change. This proposal aims to address this specific refactoring scenario as
how new code is written is a less relevant use-case for this proposal. Still, new code can
benefit by being more explicit, when applicable.
Syntax and Semantics
This proposal builds on Go's existing multi-value assignment rules, adding the ability to mix
:=
and=
operators:The rules remain straightforward:
=
for variables already in scope (like current assignments):=
for new variables (like current short declarations)This maintains Go's current semantics while enabling cleaner refactoring operations.
Rationale
Refactoring Go code frequently requires moving variable declarations and changing code structure. These changes often impact the declaration status of variables, leading to cascading modifications:
:=
may now be declared earliervar
declarations:=
to=
at usage sitesEach of these required changes introduces potential for errors and increases cognitive load during what should be simple refactoring operations. These refactoring challenges have been well-documented in multiple Go issue discussions (#30318, #31064), showing that developers consistently encounter these pain points across different codebases and over many years.
The proposal preserves Go's fundamental principle that
:=
declares new variables. Rather thanexpanding
:=
to cover every possible declaration scenario, it specifically addresses the commonrefactoring case where some variables in a multi-variable statement are new while others already
exist. This maintains the simplicity of
:=
for new variables while reducing friction during code maintenance.This explicit syntax helps prevent accidental variable shadowing, a common source of bugs in current Go code. Consider the current behavior:
The proposed syntax makes variable reuse explicit when that is the developer's intent:
Real-world Impact
The need for this change is demonstrated by developer experiences shared across multiple Go issue discussions.
From Issue #31064: Variable Declaration and Resource Management
From Issue #30318: Refactoring Experience
From Community Discussions
The broader Go community has repeatedly encountered these challenges, as evidenced by discussions across multiple platforms. Developers frequently express confusion about variable declaration patterns, particularly in error handling scenarios.
From Stack Overflow, developers highlight how the current behavior can lead to subtle bugs:
In a Reddit discussion, experienced developers express frustration with the current limitations:
The Go Forum discussions reveal that even seasoned developers struggle with these constraints in their daily work:
These experiences demonstrate that the challenges this proposal addresses represent real friction in day-to-day Go development.
Benefits
Simplifies Common Refactoring Operations:
Reduces Cognitive Load:
Improves Code Maintainability:
Preserves Code Structure:
Improved Error Messaging:
These clearer error messages help developers understand and correct common mistakes while learning the language.
The benefits of this approach have been validated by multiple developer discussions (#30318, #31064), where the ability to clearly express intent during refactoring was consistently highlighted as a key need.
Mental Model
Current Complexity
When refactoring code that uses
:=
, developers must maintain multiple mental contexts simultaneously. They must track which variables are already declared in the current scope, remember which variables need explicit declarations, consider whether to split multi-variable assignments, and manage the coordination of multiple changes while preserving correctness. This cognitive overhead diverts attention from the actual refactoring task at hand.Proposed Simplification
This proposal reduces cognitive load during refactoring by enabling developers to indicate their intent explicitly for each variable. The syntax allows single-line changes that preserve code structure while maintaining clear communication about variable lifecycle. This approach eliminates the need to coordinate multiple changes across different parts of the code.
Impact on Code Understanding
The proposed syntax makes variable handling more explicit. When developers see
var1 =, var2 := func()
, they immediately understand thatvar1
exists whilevar2
is new. This eliminates the need to search for previous declarations and enables local reasoning about variable lifecycle. The syntax reduces the mental juggling required during refactoring operations.Impact on Development Workflows
The proposed syntax specifically addresses several workflow challenges identified in #31064:
Code Review Impact:
The changes become more localized, with clearer intent during refactoring changes. Reviewers can more easily verify correctness because fewer lines need modification.
IDE Integration:
The explicit syntax enables simpler refactoring operations, clearer static analysis opportunities, and better quick-fix suggestions.
Maintenance Benefits:
The proposal reduces the chance of introducing bugs during refactoring, preserves logical grouping of operations, and maintains clear connections between variable declarations and their usage.
Impact on Development Workflows
The proposed syntax specifically addresses several workflow challenges identified in #31064:
Code Review Impact:
IDE Integration:
Maintenance Benefits:
Costs
The implementation costs of this proposal are minimal and well-contained. The changes required are straightforward and build upon existing Go language features without introducing fundamental complexities.
The costs are minimal because:
Implementation
The implementation would require changes to:
Examples
The following examples — the first three (3) of which are from the Go Standard Library —
demonstrate real refactoring challenges found in the Go code.
While the standard library examples come from just a few files in only one standard package —
net/http
— similar patterns appear throughout the standard library, indicating these arecommon refactoring needs rather than isolated cases.
Example 1: Context Deadline Management
This example shows how refactoring context management often requires splitting logically related operations.
Source: net/http/client.go#L365-L367
This pattern demonstrates key benefits:
Example 2: Request Parsing with Error Handling
This example illustrates how refactoring error handling often forces separation of related variable handling.
Source: net/http/fs.go#L139-L148
This pattern shows how the proposal:
Example 3: Request Body Management
This example shows how refactoring can force separation of error handling from the operations that produce them.
Source: net/http/transport.go#L618-L619
This pattern demonstrates how the proposal:
Example 4: Resource Management Pattern
This example demonstrates how refactoring resource cleanup code often requires separating declarations:
These real-world examples from the standard library demonstrate how this proposal addresses actual refactoring pain points in production Go code. Each case shows how the current syntax forces artificial separation of related operations during refactoring, and how the proposed syntax would maintain better code organization while reducing cognitive overhead.
Compatibility
This proposal maintains complete compatibility with existing Go code:
Source Compatibility
:=
and=
behaviors remain unchangedBackward Compatibility
Since the Go team has moved away from breaking changes:
Future Extensions
This proposal is intentionally limited in scope to address a specific pain point in Go development. No future extensions are envisioned as part of this proposal. The goal is to solve the concrete problem of mixed declaration and assignment in multi-variable statements while maintaining Go's simplicity and clarity.
Questions for Discussion
The proposal itself is complete and does not require resolution of these tooling questions, which can be addressed as follow-up enhancements.
Language Spec Changes
The language specification would require changes to Section 11.4 "Short variable declarations" to accommodate mixed declaration-assignment operators in multi-variable assignments. The primary modification would extend the current short variable declaration rules to allow mixing of
=
and:=
operators within the same statement."The current spec language in Section 11.4 states that short variable declarations must declare all non-blank variables. This would be modified to allow a mixture of declarations and assignments, where each variable on the left side of the assignment would be explicitly marked with either
=
(for existing variables) or:=
(for new variables).The modified specification would add language similar to:
"A mixed declaration-assignment statement may declare some new variables and assign to existing ones using a combination of
=
and:=
operators. Each variable on the left side must be explicitly marked with either=
(indicating assignment to an existing variable) or:=
(indicating declaration of a new variable). The expression list on the right must return the same number of values as the number of variables on the left. Type inference for newly declared variables follows the same rules as current short variable declarations."For example, this syntax would be valid:
The type-checking rules would remain unchanged:
:=
must not already exist in the current scope=
must already exist in the current scopeThis change preserves Go's existing scoping rules and type system while enabling more explicit handling of variable declarations and assignments in multi-variable statements.
Informal Change
Today we're going to look at a proposed improvement to Go's variable declaration and assignment syntax. As you know, in Go we use
:=
to declare new variables and=
to assign values to existing variables. But what happens when we're working with multiple variables at once, and some are new while others already exist? Currently, we have to split these operations or add explicit variable declarations, which can make our code more complex than it needs to be.Let's look at a common scenario. Imagine you're writing code that makes an HTTP request and processes the response. You might start with:
Later, as you refactor your code, you might need to reuse the response variable but declare a new error. In current Go, you'd need to write:
With the proposed change, you could write this more directly:
The syntax tells Go exactly what you want: "use the existing response variable, but declare err as new." It's like having the best of both worlds – you can mix and match
:=
for new variables and=
for existing ones in the same statement.This might seem like a small change, but it makes a big difference when you're refactoring code. Instead of having to add variable declarations or split your operations across multiple lines, you can clearly express your intent in a single line. The code becomes more maintainable and easier to understand, as the syntax directly shows which variables are new and which are being reused.
Think of it as being more explicit about your intentions. Rather than Go having to guess whether you want to create new variables or reuse existing ones, you're telling it directly: "this one is new, that one already exists." This clarity helps both the compiler and other developers understand your code better.
Is this change backward compatible?
Yes, this change is fully backward compatible with existing Go code. The proposal introduces new syntax without modifying the behavior of any existing Go code. All current Go programs will continue to compile and run exactly as they do today.
Consider this existing code:
After the change, this code continues to work identically. The new syntax only takes effect when explicitly combining
=
and:=
operators:The proposal maintains Go's commitment to backward compatibility by adding capabilities without breaking existing code or changing current behavior. No code changes are required to adopt Go versions that include this feature.
Orthogonality: How does this change interact or overlap with existing features?
This proposal interacts minimally with existing Go features, as it builds directly on Go's established variable declaration and assignment operators. The change maintains the current semantics of both
:=
and=
while enabling their combined use in multi-variable statements. This approach preserves Go's existing scoping rules, type system, and variable lifecycle management.The proposal complements Go's current error handling patterns without modifying them. It works seamlessly with Go's multiple return values, defer statements, and package-level variables. The change has no impact on Go's type inference system, maintaining all existing type-checking rules.
This is not a performance-focused change, and will have no impact on performance; pro or con. The goal is to improve code clarity and reduce cognitive load during refactoring operations. The compilation and runtime behavior of programs would remain identical to their current implementation. There should be performance impact at runtime and I highly doubt any impact at compile time since the change only affects how variables are declared and assigned during compilation, not how they are handled at runtime.
Would this change make Go easier or harder to learn, and why?
This change would make Go easier to learn by making variable declaration and assignment rules more consistent and explicit. Currently, new Go developers must learn that they cannot mix := and = in multi-variable statements, even though this limitation doesn't serve a clear pedagogical purpose. When they encounter situations where some variables exist and others need to be declared, they must learn various workarounds that make the code less straightforward.
The proposed syntax actually reinforces Go's principles by making variable declaration status explicit. When a developer sees
response =, err := client.Do(request)
, the code clearly communicates thatresponse
exists whileerr
is new. This explicit marking helps learners understand variable lifecycle management more clearly than the current approach, where they must either split the operation into multiple statements or add separate variable declarations.This explicitness aligns with Go's philosophy of clear, readable code. Rather than requiring developers to learn special cases and workarounds, the proposal allows them to express their intent directly. When teaching Go, instructors can now present a more coherent model: "use = for existing variables, := for new variables, and you can combine them when you need both." This consistency helps developers build a more accurate mental model of how Go handles variables.
Cost Description
The primary cost of this proposal is minimal and well-contained. It requires a small addition to Go's parser to recognize mixed operators in multi-variable assignments, along with corresponding updates to type checking logic. These changes are localized to specific components of the Go toolchain and do not affect other language features.
From a tooling perspective, IDEs and code analysis tools would need updates to properly handle the new syntax. However, since the change builds on existing operator behavior, these updates would be straightforward extensions of current variable tracking capabilities rather than complex new features.
The cognitive cost to developers is low because the proposal builds naturally on their existing understanding of Go's declaration and assignment operators. The syntax is intuitive for those who already understand Go's variable declaration rules, as it combines familiar operators in a logical way. No new operators or keywords are introduced, and developers can continue using existing patterns if they prefer.
Documentation would need minor updates to explain the new capability, but these changes would be small additions to existing variable declaration sections rather than extensive new material. The proposal's alignment with Go's current principles means that conceptual documentation would require minimal revision.
Changes to Go ToolChain
Primarily
gopls
,gofmt
, andvet
but really the underlyinggo/parser
andgo/types
packages.Performance Costs
The compile-time cost is minimal, requiring only straightforward parser and type-checker updates; there is no runtime cost as the change only affects how variables are declared during compilation.
Prototype
The implementation would require modifications to several key components of the Go compiler, with the most significant changes occurring in the parser and type checker.
In
cmd/compile/internal/syntax/parser.go
, the parser would need to be extended to recognize mixed operators in assignment statements. Currently, the parser treats:=
and=
as mutually exclusive in multi-variable assignments. The modification would allow them to be mixed by updating theparseSimpleStmt()
andparseAssignment()
functions to handle individual operator tokens for each left-hand variable. The parser would need to distinguish between statements likex =, y := expr
and traditional assignments.The type checker changes would primarily affect
cmd/compile/internal/types2/assignments.go
. The assignability checks would need to be modified to handle mixed declaration-assignment statements. For each left-hand variable, the type checker would verify that variables marked with=
exist in the current scope while those with:=
do not, maintaining Go's existing scoping rules. The check functions would need to track the declaration status of each variable independently within the same statement.The
go/types
package would require corresponding updates to support the new syntax in the type checker. This would involve modifying the Check method to handle mixed declaration-assignment statements and ensuring proper type inference for newly declared variables while maintaining type compatibility checks for existing variables.A prototype implementation could be developed in stages:
The initial prototype could focus on these core compiler changes before addressing tooling updates. This would allow for early testing and validation of the syntax while deferring modifications to tools like
gopls
andvet
.The text was updated successfully, but these errors were encountered: