Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: spec: allow using spread on arrays in function calls, assignment and return statements #62685

Closed
andig opened this issue Sep 17, 2023 · 8 comments
Labels
dotdotdot ... FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Milestone

Comments

@andig
Copy link
Contributor

andig commented Sep 17, 2023

Would you consider yourself a novice, intermediate, or experienced Go programmer?

Experienced

What other languages do you have experience with?

Recently only Go

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

Might be a bit harder to understand why returns don't allow slices

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

Couldn't find it mentioned. It might clash with discussions of omitting zero values in return statements.

Who does this proposal help, and why?

Reduce amount of code to write

What is the proposed change?

Allow using the spread operator on arrays in function calls, return statements and assignments.

Please describe as precisely as possible the change to the language.

Allow

func main() {
	words := [2]string{"choose", "blimp"}
	_, _, _, _ = demo(words...)
}

func demo(s, t string) (float64, float64, float64, error) {
    var a [3]float64
    l1, l2, _ := a...
    return a..., nil
}

What would change in the language spec?

The ... operator would get a new use case.

Please also describe the change informally, as in a class teaching Go.

Using the spread operator in

  • function calls
  • assignments
  • or return statements

allows assigning or returning a matching number of individual parameters taken from the array of length identical to the number of expected parameters.

Is this change backward compatible?

Yes

Show example code before and after the change.

Before:

var res []float64
for i := 0; i < 3; i++ {
    res = append(res, float64(encoding.Float32LswFirst(b[4*i:])))
}

return res[0], res[1], res[2], nil

After (changed to array since slice wouldn't work):

var res [3]float64
for i := 0; i < 3; i++ {
    res[i]= float64(encoding.Float32LswFirst(b[4*i:]))
}

return res..., nil

What is the cost of this proposal? (Every language change has a cost).

Implementation, discussion of slices of known length vs arrays.

How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?

?

What is the compile time cost?

?

What is the run time cost?

?

Can you describe a possible implementation?

No

Do you have a prototype? (This is not required.)

No

How would the language spec change?

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

No

Is the goal of this change a performance improvement?

No

Does this affect error handling?

No :)

Is this about generics?

No

@andig andig added the Proposal label Sep 17, 2023
@gopherbot gopherbot added this to the Proposal milestone Sep 17, 2023
@seankhliao seankhliao added LanguageChange Suggested changes to the Go language v2 An incompatible library change labels Sep 17, 2023
@apparentlymart
Copy link

When used with function arguments, the ... symbol cannot "spread" into distinct arguments today. For example:

func main() {
	words := [4]string{"choose", "blimp", "sleep", "blue"}
	a(words...)
}

func a(w1, w2, w3, w4 string) {
}

The above returns the compile error cannot use ... in call to non-variadic a at the callsite. (Playground).

It seems inconsistent to permit that sort of destructuring assignment for return values and "normal" assignments but not for the implied assignments to function parameters in calls. Are you meaning to propose to support this in function call argument lists too?


Note that it isn't currently valid to use arrays in that location either. It only works if the argument given is a slice and it's being assigned to a parameter that uses ... itself:

func main() {
	words := [4]string{"choose", "blimp", "sleep", "blue"}
	a(words[:]...)
}

func a(words ...string) {
}

...but perhaps that was the point you were intending to make in calling out the distinction between slices and arrays in your proposal.

@andig
Copy link
Contributor Author

andig commented Sep 17, 2023

Are you meaning to propose to support this in function call argument lists too?

Indeed, yes. Seems I forgot that use case when I was looking at current code i have. Update above.

...but perhaps that was the point you were intending to make in calling out the distinction between slices and arrays in your proposal.

I'm wondering if that is actually worth another proposal: allow spread to spread arrays similar to slices where variadic parameters are targetd. In context of this proposal- targeted at arrays- this would rather be equivalent to using it for spreading to a well-known number of identically-typed arguments:

func main() {
	words := [2]string{"choose", "blimp"}
	a(words...)
}

func a(w1, w2 string) {
}

@apparentlymart
Copy link

Thanks for the extra information, @andig.

The specification currently states (in Passing arguments to ... parameters) that passing slice... into a ...T variadic argument passes the slice header directly, and therefore does not allocate a new backing array.

Given that, I assume that it were made possible to refer to an array directly in that position, like this...

func main() {
	words := [4]string{"choose", "blimp", "sleep", "blue"}
	a(words...) // words is [4]string here, not []string
}

func a(words ...string) {
}

...then the compiler would behave as if the call were a(words[:]...), creating a new slice of length and capacity 4 using words as its backing array.


Continuing that analogy, let's consider your latest example:

func main() {
	words := [2]string{"choose", "blimp"}
	a(words...)
}

func a(w1, w2 string) {
}

Would you say that in this case the compiler would treat the call as if a(words[0], words[1])? Based on the assumption I stated above, I would expect that:

  • If words is an array as shown here, the compiler can check that these implied indices are all in range for the array, and so fail at compile time if there are not enough.
  • If words were instead a slice, the compiler would generate slice index code that would panic at runtime if the slice were not long enough, just as words[2] would do for a words of length 2.

I think this model of implied assignment of individual indices isn't complete because I assume you'd also want the assignment to fail if there were too many elements. Again with an array that seems trivially decidable at compile time. For slices, would that also be a panic?

(I realize that in the original proposal text you have implicitly ruled out using slices by only describing the behavior for arrays. I'm only poking at behavior for slices because in these comments we've been discussing the generalization of ... to work with both arrays and slices, and so I'm thinking through the implications of making that a symmetrical generalization, where both arrays and slices would be valid in all situations. In practice it might make more sense to forbid slices for this sort of destructuring assignment, but it's unfortunate to make that sort of exception so I think worth exploring alternatives.)

@apparentlymart
Copy link

A further note, based on more spec reading:

The slice operation is defined to work both on arrays directly and on pointers to arrays, and so I assume for consistency it would also become valid to place a pointer to an array before ... and have that implicitly dereference the array as the slice operator does.

@andig
Copy link
Contributor Author

andig commented Sep 17, 2023

The proposal targets arrays only as I think we must maintain compile time safety.

@andig andig changed the title proposal: allow using spread on arrays in assignment and return statements proposal: allow using spread on arrays in function calls, assignment and return statements Sep 17, 2023
@andig andig changed the title proposal: allow using spread on arrays in function calls, assignment and return statements proposal: spec: allow using spread on arrays in function calls, assignment and return statements Sep 17, 2023
@ianlancetaylor
Copy link
Member

It's an interesting idea but the restriction to arrays means that it won't have many uses. Also, the emoji voting is not in favor. Therefore, this is a likely decline. Leaving open for three weeks for final comments.

@apparentlymart
Copy link

Indeed, I think limiting to only arrays does help rule out some potential runtime panics but it also makes the result considerably less useful, because I think direct use of arrays is relatively uncommon and most Go programs are built to use slices except in some specialized situations such as using a fixed-size array to represent a checksum, and I don't expect it would be useful to destructure those.

Somehow making it work for slices would make it more widely-applicable, and that was what interested me in exploring that angle more deeply above, but I think it would also feel a little out of place in Go, since that sort of dynamic dynamically-fallible destructuring is more common in dynamically-typed languages where most mistakes are detected at runtime. That's not the sort of design I'd expect from a language like Go.

@findleyr
Copy link
Member

findleyr commented Nov 1, 2023

No change in consensus.

@findleyr findleyr closed this as completed Nov 1, 2023
@griesemer griesemer closed this as not planned Won't fix, can't repro, duplicate, stale Nov 1, 2023
@golang golang locked and limited conversation to collaborators Oct 31, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
dotdotdot ... FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Projects
None yet
Development

No branches or pull requests

8 participants
@apparentlymart @andig @ianlancetaylor @griesemer @gopherbot @seankhliao @findleyr and others