Go Slice Expressions: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(19 intermediate revisions by the same user not shown)
Line 4: Line 4:
* https://go.dev/ref/spec#Slice_expressions
* https://go.dev/ref/spec#Slice_expressions
=Overview=
=Overview=
Slice expressions <code>[i:j]</code> can be used on array or other slices.


A slice expression applied to a slice creates a new slice that shares the underlying array with the original slice.
A slice expression applied to a slice creates a '''new''' slice that '''shares the underlying array''' with the original slice. Applying a slice expression to a slice is called '''reslicing'''.


<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
Line 13: Line 14:


<font size=-1.5>
<font size=-1.5>
    ┌─────────────────────┐
  ss │pointer,len=10,cap=10│
    └──│──────│──────│────┘
        │      └──────┴──────────────────────────┐
        └──────────────────────┐                │
                              ▼                ▼
                              ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
              derlying array  └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
                              0 1 2 3 4 5 6 7 8 9
                                    ▲  ▲      ▲
        ┌───────────────────────────┘  │      │
        │      ┌────────────────────────┘      │
        │      │    ┌──────────────────────────┘
    ┌───│──────│─────│────┐   
ss2 │ pointer,len=3,cap=7 │
    └─────────────────────┘


          ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
          └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
          0 1 2 3 4 5 6 7 8 9
</font>
</font>
The slice expression creates a different slice that provides access to the elements with indexes 3, 4, 5 of the underlying array. Both slices <code>ss</code> and <code>ss2</code> provide access to those elements and changes operated via one slice are visible to the other.
The slice expression creates a different slice that provides access to the elements with indexes 3, 4, 5 of the underlying array. Both slices <code>ss</code> and <code>ss2</code> provide access to those elements and changes operated via one slice are visible to the other.


The length of the second slice is the number of elements defined by the slice expression (3). However, since the slices share the same underlying array, the capacity of the second slice is the number of elements from the element with index 3 to the end of the underlying array: 7.
The length of the second slice is the number of elements defined by the slice expression (3). However, since the slices share the same underlying array, the capacity of the second slice is the number of elements from the element with index 3 to the end of the underlying array: 7.
=Shorthands=
==<tt>[i:]</tt>==
Equivalent with <code>[i:len(slice)]</code>.
==<tt>[:i]</tt>==
Equivalent with <code>[0:i]</code>.
==<tt>[:]</tt>==
Means the slice itself, which is useful when slicing an array. Equivalent with <code>[0:len(a)]</code>. To turn an entire array into a slice:
<syntaxhighlight lang='go'>
array[:]
</syntaxhighlight>
=Idiomatic Use Cases=
==Truncate a Slice==
===Drop the First Element===
<syntaxhighlight lang='go'>
slice := slice[1:len(slice)]
</syntaxhighlight>
Equivalent with:
<syntaxhighlight lang='go'>
slice := slice[1:]
</syntaxhighlight>
===Drop the Last Element===
<syntaxhighlight lang='go'>
slice := slice[0:len(slice)-1]
</syntaxhighlight>
=<span id='Three-Index_Slice_Expression'></span>Three-Index Slice Expression <tt>[low:high:high_for_capacity]</tt>=
The third index specified with the slice expression restricts the capacity of the newly created slice.
<syntaxhighlight lang='go'>
newSliceIdentifier := oldSliceIdentifier[low:high:high_for_capacity]
</syntaxhighlight>
The "low" and "high" have the same semantics as in the case of the regular slice expression. "high_for_capacity" is the index where to end the underlying array of the slice. The underlying array of the new slice can use as "capacity" elements all the elements up to, but not including the "high_for_capacity" index.
Example:
<syntaxhighlight lang='go'>
ss := []string {"a", "b", "c", "d", "e"} // creates a slice with length 5 and capacity 5
ss2 := ss[1:2:3] // creates a slice with length 1, containing 'b", and the capacity 2
</syntaxhighlight>
Note that "high_for_capacity" must fall inside the underlying array backing "a", or, at most, must fall right outside the array, which means that the whole array will be used for capacity, otherwise we get "slice bounds out of range". The slices keep sharing the memory areas corresponding to their backing arrays, so <code>b[0]="x"</code> will cause <code>a[1]</code> to become "x".
The three-index slice expression is useful to create slices whose capacity is the same as the length, so the first <code>append()</code> operation creates a new underlying array, thus completely detaching the slices - which is a good thing. From a practical perspective, it is a good idea to always use the "three-index slice expression" where the "high" and "high_for_capacity" coincide:
<syntaxhighlight lang='go'>
ss2 := ss[low, high, high]
</syntaxhighlight>

Latest revision as of 22:27, 26 August 2024

Internal

TODO

Overview

Slice expressions [i:j] can be used on array or other slices.

A slice expression applied to a slice creates a new slice that shares the underlying array with the original slice. Applying a slice expression to a slice is called reslicing.

ss := make([]int, 10) // declare a slice with length and capacity 10, initialized with 0
ss2 := ss[3:6]

    ┌─────────────────────┐
 ss │pointer,len=10,cap=10│
    └──│──────│──────│────┘
       │      └──────┴──────────────────────────┐
       └──────────────────────┐                 │
                              ▼                 ▼
                             ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
             derlying array  └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
                              0 1 2 3 4 5 6 7 8 9
                                    ▲   ▲       ▲
        ┌───────────────────────────┘   │       │
        │      ┌────────────────────────┘       │
        │      │     ┌──────────────────────────┘
    ┌───│──────│─────│────┐    
ss2 │ pointer,len=3,cap=7 │
    └─────────────────────┘

The slice expression creates a different slice that provides access to the elements with indexes 3, 4, 5 of the underlying array. Both slices ss and ss2 provide access to those elements and changes operated via one slice are visible to the other.

The length of the second slice is the number of elements defined by the slice expression (3). However, since the slices share the same underlying array, the capacity of the second slice is the number of elements from the element with index 3 to the end of the underlying array: 7.

Shorthands

[i:]

Equivalent with [i:len(slice)].

[:i]

Equivalent with [0:i].

[:]

Means the slice itself, which is useful when slicing an array. Equivalent with [0:len(a)]. To turn an entire array into a slice:

array[:]

Idiomatic Use Cases

Truncate a Slice

Drop the First Element

slice := slice[1:len(slice)]

Equivalent with:

slice := slice[1:]

Drop the Last Element

slice := slice[0:len(slice)-1]

Three-Index Slice Expression [low:high:high_for_capacity]

The third index specified with the slice expression restricts the capacity of the newly created slice.

newSliceIdentifier := oldSliceIdentifier[low:high:high_for_capacity]

The "low" and "high" have the same semantics as in the case of the regular slice expression. "high_for_capacity" is the index where to end the underlying array of the slice. The underlying array of the new slice can use as "capacity" elements all the elements up to, but not including the "high_for_capacity" index.

Example:

ss := []string {"a", "b", "c", "d", "e"} // creates a slice with length 5 and capacity 5
ss2 := ss[1:2:3] // creates a slice with length 1, containing 'b", and the capacity 2

Note that "high_for_capacity" must fall inside the underlying array backing "a", or, at most, must fall right outside the array, which means that the whole array will be used for capacity, otherwise we get "slice bounds out of range". The slices keep sharing the memory areas corresponding to their backing arrays, so b[0]="x" will cause a[1] to become "x".

The three-index slice expression is useful to create slices whose capacity is the same as the length, so the first append() operation creates a new underlying array, thus completely detaching the slices - which is a good thing. From a practical perspective, it is a good idea to always use the "three-index slice expression" where the "high" and "high_for_capacity" coincide:

ss2 := ss[low, high, high]