Description
I propose to allow the second index of 3-index slice expressions to be elided, with the default value “min(original length, new capacity)”.
This is based on the observation that the 3-index slice form is almost always used with the new length set to either 0
or the same expression as the new capacity, and the new capacity set to either exactly the original length or value known not to exceed it (such as in numerous fixes for #34972).
The specific default is chosen for the following properties:
- Unlike defaulting to the the original length alone, it is guaranteed not to panic for the expression
a[::k]
for anyk
≤cap(a)
. - Unlike defaulting to the new capacity alone, it does not preclude a future change from establishing the intuitive identity
x[::] ≡ x
.
Template answers
- Would you consider yourself a novice, intermediate, or experienced Go programmer?
Experienced.
- What other languages do you have experience with?
C, C++, Standard ML, OCaml, Rust, JavaScript, Python, Bash, π-calculus, probably a few others I've missed.
- Would this change make Go easier or harder to learn, and why?
Could go either way: 3-index slices are already relatively obscure, but this proposal arguably makes them more closely match the intuitive behavior.
- Has this idea, or one like it, been proposed before?
This form was discussed in the original design doc, but explicitly deferred for later discussion. Now that we have released 12 intervening Go versions, I believe we have enough experience to continue that discussion.
It is not clear to me whether this point of the design has been revisited since Go 1.2. (If so, the original issue #1642 has no cross-references to that discussion.)
- Who does this proposal help, and why?
Anyone who uses append
and doesn't enjoy debugging aliasing bugs.
This proposal makes it easier (less verbose) to append
to a slice that is never otherwise mutated, without risking overlapping mutations to the portion of the slice beyond its length.
- What is the proposed change?
Allow the second (high
) index of a 3-index “full slice expression” to be elided.
Specifically, for an expression of the form a[low : high : max]
, if the high
term is elided, it should default to min(len(a)
, max
).
This corresponds to cases 3 and 6 in the original design doc. However, this proposal does not allow the third element to be elided: we do not need to choose between those two cases at this time.
- What would change in the language spec?
- Only the first index may be omitted; it defaults to 0.
+ The first and/or second index may be omitted.
+ The first index defaults to 0.
+ The second index defaults to either `max` or the length of the sliced operand, whichever is smaller.
- Please also describe the change informally, as in a class teaching Go.
If the middle index of a 3-index slice expression is omitted, it defaults to either the length of the original slice or the new capacity, whichever is smaller.
- Is this change backward compatible?
Yes. It is explicitly called out as a possibility in the Go 1.2 design doc, and I don't believe any change in the interim has rendered it impossible.
- Show example code before and after the change.
This example is from real code (in #38077), fixing an aliasing bug.
Unfixed:
go/src/cmd/go/internal/generate/generate.go
Line 441 in 2975b27
Fixed with the language as defined in Go 1.14:
cmd.Env = append(cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)], g.env...)
Under this proposal:
cmd.Env = append(cfg.OrigEnv[::len(cfg.OrigEnv)], g.env...)
- What is the cost of this proposal? (Every language change has a cost).
- How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
The compiler and static analysis tools would need to be upgraded to accept the new form.
I believe the change to the compiler would be minimal.
go/ast.SliceExpr
already allows a nil Expr
in the High
position, so other syntax-based tools would be unaffected unless they assume SliceExpr
invariants beyond what is promised in the documentation.
- What is the compile time cost?
Negligible.
- What is the run time cost?
None: an equivalent 3-index slice can already be written today.
- Can you describe a possible implementation?
Yes: assign the 3rd index to a temporary, then use that temporary (and a trivial min
calculation) for both the 2nd and 3rd indices.
- Do you have a prototype? (This is not required.)
No.
- How would the language spec change?
Objection: asked and answered.
- Orthogonality: how does this change interact or overlap with existing features?
It makes 3-index slices more consistent with 2-index slices, for which the high
term may already be elided.
- Is the goal of this change a performance improvement?
No.
- Does this affect error handling?
No.
- Is this about generics?
No.