Skip to content

proposal: Allow implicit slice conversion in append operations #73509

@Tec-Wang

Description

@Tec-Wang

Go Programming Experience

Intermediate

Other Languages Experience

No response

Related Idea

  • Has this idea, or one like it, been proposed before?
  • Does this affect error handling?
  • Is this about generics?
  • Is this change backward compatible? Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit

Has this idea, or one like it, been proposed before?

No

Does this affect error handling?

No

Is this about generics?

No

Proposal

Proposal: Allow implicit slice conversion in append operations

Abstract:

This proposal suggests allowing implicit conversion from concrete type slices to interface type slices in append operations, matching the behavior already allowed in for-range loops.

Background:

Go currently allows implicit conversion from concrete types to interface types in most expressions, including:

  • Assignment operations
  • Parameter passing
  • Return values
  • Individual element operations in for-range loops
    However, Go prohibits implicit conversion of slice types in append operations, forcing developers to use for-loops as a workaround.

Motivation

The current behavior creates an inconsistency in the language. Consider this example:

// This works - implicit conversion from *struct1 to interface1
for i, s := range structs {
    interface1s[i] = s
}

// This doesn't work - implicit conversion from []*struct1 to []interface1
interface1s = append(interface1s, structs...)

When developers encounter this limitation, they typically fall back to this workaround:

// The workaround
for _, s := range structs {
    interface1s = append(interface1s, s)
}

This workaround:

  1. Has identical runtime characteristics to what's being disallowed
  2. Forces developers to write more verbose code
  3. Creates an inconsistency in the language design

Proposal

Allow implicit conversion of slice types in append operations when:

  • The element type of the source slice can be implicitly converted to the element type of the target slice
  • The conversion meets the same rules as existing implicit conversions
    This would make the following code valid:
interface1s = append(interface1s, structs...)

Rationale

The original reasons for prohibiting this conversion likely included concerns about:

  1. Performance and memory allocation costs
  2. Type safety
  3. Making conversion costs explicit
    However, these concerns don't justify the inconsistency:
  4. The loop-based workaround that developers already use has the same performance characteristics
  5. The type conversion is still type-safe, as it follows the rules of interface assignment
  6. The conversion costs are still explicit in a for-loop, just more verbosely expressed

Implementation

The compiler would be modified to:

  1. Check if the element type of the variadic argument in an append can be implicitly converted to the element type of the target slice
  2. If so, generate the code to perform the conversions, equivalent to what would be generated for a for-loop

Language Spec Changes

The language specification would need to be updated in the section describing the built-in append function. Currently, the spec states:

The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

The function's first parameter slice must be a slice of type []T, and the rest are T values to append to the slice. The resulting value of append is a slice of type []T with the same underlying type as slice, and length and capacity as appropriate to accommodate the additional values.

If the capacity of slice is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.

If the first parameter slice is nil, it is treated like a slice of length 0.

As a special case, if the second argument is a string and the slice element type is byte, append appends the bytes of the string.
The spec would be amended to add a new paragraph describing the implicit type conversion for interface types:
As a special case, if the second argument is a slice of type []S and the slice element type T is an interface type that S implements, append behaves as if each element of []S were individually appended to the slice after being implicitly converted to type T. This is equivalent to:

for , x := range anotherSlice {
slice = append(slice, x)
}

Where the implicit conversion from S to T occurs during the append operation of each element.
This change would make the behavior of append consistent with other operations in Go that allow implicit conversion to interface types.

Informal Change

No response

Is this change backward compatible?

Compatibility

This proposal only adds behavior and does not break existing code.

Orthogonality: How does this change interact or overlap with existing features?

No response

Would this change make Go easier or harder to learn, and why?

No response

Cost Description

No response

Changes to Go ToolChain

No response

Performance Costs

No response

Prototype

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    LanguageChangeSuggested changes to the Go languageLanguageChangeReviewDiscussed by language change review committeeLanguageProposalIssues describing a requested change to the Go language specification.Proposal

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions