go / expert
Snippet
Kapazitätsbegrenzte Slices über den Drei-Index-Ausdruck
Ein zweistelliger Slice s[low:high] erbt die volle Kapazität des Backing-Arrays bis ans Ende — ein späteres append in die Rückgabe kann unbemerkt Speicher überschreiben, auf den der Aufrufer noch zugreift. Die dreistellige Form s[low:high:max] begrenzt die Kapazität explizit auf max-low. Sobald sie voll ist, muss append ein neues Array allokieren; Mutationen bleiben isoliert. Das ist die Standardabsicherung beim Herausgeben von Sub-Slices aus Buffer-Pools, Request-Bodies oder geteilten Frames, wenn der Konsument das Eltern-Array nicht antasten darf.
snippet.go
go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package mainimport "fmt"func header(buf []byte) []byte {// s[low:high:max] — cap becomes max-low, so append on// the result cannot stomp the tail of buf in-place.return buf[0:4:4]}func main() {packet := []byte{0xCA, 0xFE, 0xBA, 0xBE,'p', 'a', 'y', 'l', 'o', 'a', 'd'}h := header(packet)fmt.Printf("len=%d cap=%d\n", len(h), cap(h))h = append(h, 0xFF) // allocates a fresh backing arrayfmt.Printf("packet[:4]=%x\n", packet[:4])}
Erklärung
1
return buf[0:4:4]
low=0, high=4, max=4 → Länge 4, Kapazität 4. Die Rückgabe ist von Anfang an voll — jedes append zwingt zu einer Neuallokation.
2
h := header(packet)
Ohne den dritten Index hätte h cap 11 und teilte sich Speicher mit packet — ein append könnte die Nutzlast verfälschen.
3
h = append(h, 0xFF)
Da cap == len, lässt die Laufzeit h in ein neues Backing-Array wachsen; die Bytes 4..10 in packet bleiben unangetastet.
4
fmt.Printf("packet[:4]=%x\n", packet[:4])
Zeigt die Isolation: auch nach dem Anhängen an h bleibt das Eltern-packet unverändert — der Vertrag, den jede Buffer-Sharing-API braucht.