<style> .reveal, .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { font-family:Arial, Microsoft JhengHei; } .textleft { text-align:left; } .reveal blockquote, .reveal pre, .reveal section img { width: 100% } .reveal .progress { height: 14px !important; } .progress span { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAMCAIAAAAs6UAAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUNCQzIyREQ0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUNCQzIyREU0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQ0JDMjJEQjRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQ0JDMjJEQzRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PovDFgYAAAAmSURBVHjaYvjPwMAAxjMZmBhA9H8INv4P4TPM/A+m04zBNECAAQBCWQv9SUQpVgAAAABJRU5ErkJggg==) repeat-x !important; } .progress span:after, .progress span.nyancat { content: ""; background: url(data:image/gif;base64,R0lGODlhIgAVAKIHAL3/9/+Zmf8zmf/MmZmZmf+Z/wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpDMkJBNjY5RTU1NEJFMzExOUM4QUM2MDAwNDQzRERBQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREIzOEIzMzRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREIzOEIzMjRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM1QkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMyQkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkECQcABwAsAAAAACIAFQAAA6J4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93bqCA7f7TFaYoIFAMMwczB5EkTzJllEUttmIGoG5bfPBjDawD7CsJC67uWcv2CRov929C/q2ZpcBbYBmLGk6W1BRY4MUDnMvJEsBAXdlknk2fCeRk2iJliAijpBlEmigjR0plKSgpKWvEUheF4tUZqZID1RHjEe8PsDBBwkAIfkECQcABwAsAAAAACIAFQAAA6B4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93TqS40XiKSYgTLBgIBAMqE/zmQSaZEzns+jQ9pC/5dQJ0VIv5KMVWxqb36opxHrNvu9ptPfGbmsBbgSAeRdydCdjXWRPchQPh1hNAQF4TpM9NnwukpRyi5chGjqJEoSOIh0plaYsZBKvsCuNjY5ptElgDyFIuj6+vwcJACH5BAkHAAcALAAAAAAiABUAAAOfeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GKifWaddvNQAtszXYCxgR/Zy5jYTFeXmSDiIZGdQEBd06QSBQ5e4cEkE9nnZQaG2J4F4MSLx8rkqUSZBeurhlTUqsLsi60DpZxSWBJugcJACH5BAkHAAcALAAAAAAiABUAAAOgeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GuifWaddvNwMkZtmY7AWMEgGcKY2ExXl5khFMVc0Z1AQF3TpJShDl8iASST2efloV5JTyJFpgOch8dgW9KZxexshGNLqgLtbW0SXFwvaJfCQAh+QQJBwAHACwAAAAAIgAVAAADoXi63P7wmUmrnVGOzbvfRsYYXGGe6MmF4kEOaSGYMwq2LizHfDGwIlLPNKGZfi6gZmggEAy2iVPZEKZqzakq+1xUFFYe90lxTsHmim6HGpvf3eR7skYJ3PC5tyystc0AboFnVXQ9XFJTZIQOYUYFTQEBeWaSVF4bbCeRk1meBJYSL3WbaReMIxQfHXh6jaYXsbEQni6oaF21ERR7l0ksvA0JACH5BAkHAAcALAAAAAAiABUAAAOeeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMfFlA4hTITEMxkIBMOuADwmhzqeM6mashTCXKw2TVKQyKuTRSx2wegnNkyJ1ozpOFiMLqcEU8BZHx6NYW8nVlZefQ1tZgQBAXJIi1eHUTRwi0lhl48QL0sogxaGDhMlUo2gh14fHhcVmnOrrxNqrU9joX21Q0IUElm7DQkAIfkECQcABwAsAAAAACIAFQAAA6J4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93bqCA7f7TFaYoIFAMMwczB5EkTzJllEUttmIGoG5bfPBjDawD7CsJC67uWcv2CRov929C/q2ZpcBbYBmLGk6W1BRY4MUDnMvJEsBAXdlknk2fCeRk2iJliAijpBlEmigjR0plKSgpKWvEUheF4tUZqZID1RHjEe8PsDBBwkAIfkECQcABwAsAAAAACIAFQAAA6B4umv+MDpG6zEj682zsRaWFWRpltoHMuJZCCRseis7xG5eDGp93TqS40XiKSYgTLBgIBAMqE/zmQSaZEzns+jQ9pC/5dQJ0VIv5KMVWxqb36opxHrNvu9ptPfGbmsBbgSAeRdydCdjXWRPchQPh1hNAQF4TpM9NnwukpRyi5chGjqJEoSOIh0plaYsZBKvsCuNjY5ptElgDyFIuj6+vwcJACH5BAkHAAcALAAAAAAiABUAAAOfeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GKifWaddvNQAtszXYCxgR/Zy5jYTFeXmSDiIZGdQEBd06QSBQ5e4cEkE9nnZQaG2J4F4MSLx8rkqUSZBeurhlTUqsLsi60DpZxSWBJugcJACH5BAkHAAcALAAAAAAiABUAAAOgeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMd8MbAiUu802flYGIhwaCAQDKpQ86nUoWqF6dP00wIby572SXE6vyMrlmhuu9GuifWaddvNwMkZtmY7AWMEgGcKY2ExXl5khFMVc0Z1AQF3TpJShDl8iASST2efloV5JTyJFpgOch8dgW9KZxexshGNLqgLtbW0SXFwvaJfCQAh+QQJBwAHACwAAAAAIgAVAAADoXi63P7wmUmrnVGOzbvfRsYYXGGe6MmF4kEOaSGYMwq2LizHfDGwIlLPNKGZfi6gZmggEAy2iVPZEKZqzakq+1xUFFYe90lxTsHmim6HGpvf3eR7skYJ3PC5tyystc0AboFnVXQ9XFJTZIQOYUYFTQEBeWaSVF4bbCeRk1meBJYSL3WbaReMIxQfHXh6jaYXsbEQni6oaF21ERR7l0ksvA0JACH5BAkHAAcALAAAAAAiABUAAAOeeLrc/vCZSaudUY7Nu99GxhhcYZ7oyYXiQQ5pIZgzCrYuLMfFlA4hTITEMxkIBMOuADwmhzqeM6mashTCXKw2TVKQyKuTRSx2wegnNkyJ1ozpOFiMLqcEU8BZHx6NYW8nVlZefQ1tZgQBAXJIi1eHUTRwi0lhl48QL0sogxaGDhMlUo2gh14fHhcVmnOrrxNqrU9joX21Q0IUElm7DQkAOw==) !important; width: 34px !important; height: 21px !important; border: none !important; float: right; margin-top: -7px; margin-right: -10px; } </style> # Go Slices Slide ###### tags: `Golang`, `簡報` --- ## Agenda - 起因 - slice in GO - 回到問題上(起因) - 延伸閱讀 - slice 上的 "gotcha" - 延伸閱讀 - slice 當參數傳遞 - 延伸閱讀 - Method receivers - 延伸閱讀 - slice 相關豆知識 --- ## 起因 ---- ### 看到了一篇 medium [Go’s append is not always thread safe](https://medium.com/@cep21/gos-append-is-not-always-thread-safe-a3034db7975) ---- <div class="textleft"> 這邊舉了兩個例子,在使用 goroutine 對同一個 slice append 會發生 race 的情況 p.s. 使用 `go test -race` 可以檢查是否會發生 race </div> ---- ### 範例一 ```go= package main import ( "sync" "testing" ) func TestAppend(t *testing.T) { x := []string{"start"} wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() y := append(x, "hello", "world") t.Log(cap(y), len(y)) }() go func() { defer wg.Done() z := append(x, "goodbye", "bob") t.Log(cap(z), len(z)) }() wg.Wait() } ``` ---- ### 結果 ```shell > go test -race . ok _/Users/kais.lin/go/goTest/slice 1.015s > go test -v z=> [start goodbye bob] y=> [start hello world] [start] --- PASS: TestAppend (0.00s) app_test.go:24: 3 app_test.go:18: 3 PASS ok _/Users/kais.lin/go/goTest/slice 0.006s ``` ---- ### 範例二 ```go= package main import ( "testing" "sync" ) func TestAppend(t *testing.T) { x := make([]string, 0, 6) wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() y := append(x, "hello", "world") t.Log(len(y)) }() go func() { defer wg.Done() z := append(x, "goodbye", "bob") t.Log(len(z)) }() wg.Wait() } ``` ---- ### 結果 ```shell > go test -race . ================== WARNING: DATA RACE Write at 0x00c4200be060 by goroutine 8: _/tmp.TestAppend.func2() /tmp/main_test.go:20 +0xcb Previous write at 0x00c4200be060 by goroutine 7: _/tmp.TestAppend.func1() /tmp/main_test.go:15 +0xcb Goroutine 8 (running) created at: _/tmp.TestAppend() /tmp/main_test.go:18 +0x14f testing.tRunner() /usr/local/Cellar/go/1.10.2/libexec/src/testing/testing.go:777 +0x16d Goroutine 7 (running) created at: _/tmp.TestAppend() /tmp/main_test.go:13 +0x105 testing.tRunner() /usr/local/Cellar/go/1.10.2/libexec/src/testing/testing.go:777 +0x16d ================== ================== WARNING: DATA RACE Write at 0x00c4200be070 by goroutine 8: _/tmp.TestAppend.func2() /tmp/main_test.go:20 +0x11a Previous write at 0x00c4200be070 by goroutine 7: _/tmp.TestAppend.func1() /tmp/main_test.go:15 +0x11a Goroutine 8 (running) created at: _/tmp.TestAppend() /tmp/main_test.go:18 +0x14f testing.tRunner() /usr/local/Cellar/go/1.10.2/libexec/src/testing/testing.go:777 +0x16d Goroutine 7 (finished) created at: _/tmp.TestAppend() /tmp/main_test.go:13 +0x105 testing.tRunner() /usr/local/Cellar/go/1.10.2/libexec/src/testing/testing.go:777 +0x16d ================== --- FAIL: TestAppend (0.00s) main_test.go:16: 2 main_test.go:21: 2 testing.go:730: race detected during execution of test FAIL FAIL _/tmp 0.901s > go test -v === RUN TestAppend z=> [hello world] y=> [hello world] [] --- PASS: TestAppend (0.00s) app_test.go:24: 2 app_test.go:18: 2 PASS ok _/Users/kais.lin/go/goTest/slice 0.005s ``` ---- SO WHY? --- ## slice in GO ---- ### 要瞭解 slice 前必須先了解 array > The slice type is an abstraction built on top of Go’s array type, and so to understand slices we must first understand arrays. ---- ### Array in GO <div class="textleft"> Array 宣告時會指定了長度和元素類型,且不需初始化,預設值為 0 </div> ```go var a [4]int a[0] = 1 i := a[0] // i == 1 a[2] == 0 // true, the zero value of the int type ``` <div class="textleft"> 在記憶體中會長這樣 </div> ![](https://blog.golang.org/go-slices-usage-and-internals_slice-array.png) ---- ### 那 slice 呢? <div class="textleft"> 為了可以更彈性的利用 Array,所以 slice 長這樣: </div> ```go type sliceHeader struct { Length int Capacity int ZerothElement *byte } ``` <div class="textleft"> - Length: slice 長度<br> - Capacity: 底層 array 的長度<br> - ZerothElement: 參考(指向)的底層 array </div> ---- 所以當你從 slice 擷取部分值時: s = s[2:4] ![](https://blog.golang.org/go-slices-usage-and-internals_slice-2.png) []byte 上到下分別為: ZerothElement, len, cap ---- 所以對 `slice` `append` 值進去時,其實是進了底層 array,但如過底層 array 不夠大時怎麼辦? ---- ```go func AppendByte(slice []byte, data ...byte) []byte { m := len(slice) n := m + len(data) if n > cap(slice) { // if necessary, reallocate // allocate double what's needed, for future growth. newSlice := make([]byte, (n+1)*2) copy(newSlice, slice) slice = newSlice } slice = slice[0:n] copy(slice[m:n], data) return slice } ``` --- ## 回到問題上(起因) ---- 回到範例一 ```go ... x := []string{"start"} ... go func() { ... y := append(x, "hello", "world") ... } go func() { ... z := append(x, "hello", "world") ... } ``` ---- <!-- .slide: data-background="#777" --> ![](https://cdn-images-1.medium.com/max/1600/1*MoOXXCuFEbYag5ZYIjgx5g.png) ---- <!-- .slide: data-background="#777" --> ![](https://cdn-images-1.medium.com/max/1600/1*VsEQuGIq9YE1__suevGPlg.png) ---- 範例二 ```go ... x := make([]string, 0, 6) ... go func() { ... y := append(x, "hello", "world") ... } go func() { ... z := append(x, "hello", "world") ... } ``` ---- <!-- .slide: data-background="#777" --> ![](https://cdn-images-1.medium.com/max/1600/1*oOJ9t3KPpbihDnMSF_pLfA.png) ---- <!-- .slide: data-background="#777" --> ![](https://cdn-images-1.medium.com/max/1600/1*jaT9M8OvuYFzyf29Qu8cbA.png) ---- ### 作者提供的解決方法 ```go package main import ( "sync" "testing" ) func TestAppend(t *testing.T) { x := make([]string, 0, 6) x = append(x, "start") wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() y := make([]string, 0, len(x)+2) y = append(y, x...) y = append(y, "hello", "world") t.Log(cap(y), len(y), y[0]) }() go func() { defer wg.Done() z := make([]string, 0, len(x)+2) z = append(z, x...) z = append(z, "goodbye", "bob") t.Log(cap(z), len(z), z[0]) }() wg.Wait() } ``` --- ## 延伸閱讀 - slice 上的 "gotcha" ---- ### 還記得我們說過<br>slice 指向(參考)著底層的 array ---- <div class="textleft"> 所以如果存了一大堆資料進 slice 而最後只為了取其中一小部分值的話,就會發生: </div> ```go var digitRegexp = regexp.MustCompile("[0-9]+") func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) // b 為 slice return digitRegexp.Find(b) // 回傳由 b(slice) 做正規畫後得到的資料 } ``` <div class="textleft"> 會導致其他的 data 依然存在底層 array 中~ </div> ---- ### 解決方法 ```go func CopyDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) b = digitRegexp.Find(b) c := make([]byte, len(b)) copy(c, b) return c } ``` --- ## 延伸閱讀 - slice 當參數傳遞 ---- ```go func AddOneToEachElement(slice []int) { for i := range slice { slice[i]++ } } func main() { buffer := []int {0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10} slice := buffer[10:20] fmt.Println("slice before ", slice) // slice before [0 1 2 3 4 5 6 7 8 9] fmt.Println("buffer before", buffer) // buffer before [0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10] AddOneToEachElement(slice) fmt.Println("slice after ", slice) // slice after [1 2 3 4 5 6 7 8 9 10] fmt.Println("buffer after ", buffer) // buffer after [0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10 10] } ``` ##### 儘管 slice 傳遞看起來像 passed by value,但因為 slice header 內含指標,所以會更新到原始參照的 array --- ## 延伸閱讀 - Method receivers ---- 想將這功能改用 receivers 的方式實作 ```go func main() { pathName := path("/usr/bin/tso") // Conversion from string to path. i := bytes.LastIndex(pathName, []byte("/")) if i >= 0 { pathName = (pathName)[0:i] } fmt.Printf("%s\n", pathName) // /usr/bin } ``` ---- > It is idiomatic to use a pointer receiver for a method that modifies a slice. > ```go type path []byte func (p *path) TruncateAtFinalSlash() { i := bytes.LastIndex(*p, []byte("/")) if i >= 0 { *p = (*p)[0:i] } } func main() { pathName := path("/usr/bin/tso") // Conversion from string to path. pathName.TruncateAtFinalSlash() fmt.Printf("%s\n", pathName) // /usr/bin } ``` ---- 但如果這邊改用 value receiver 的話 ```go type path []byte func (p path) TruncateAtFinalSlash() { i := bytes.LastIndex(p, []byte("/")) if i >= 0 { p = (p)[0:i] } fmt.Printf("%s\n", p) // /usr/bin } func main() { pathName := path("/usr/bin/tso") // Conversion from string to path. pathName.TruncateAtFinalSlash() fmt.Printf("%s\n", pathName) // /usr/bin/tso } ``` ---- 範例二: 要將 path 全轉大寫 ```go type path []byte func (p path) ToUpper() { for i, b := range p { if 'a' <= b && b <= 'z' { p[i] = b + 'A' - 'a' } } } func main() { pathName := path("/usr/bin/tso") pathName.ToUpper() fmt.Printf("%s\n", pathName) // /USR/BIN/TSO } ``` ---- ```go type path []byte func (p *path) ToUpper() { // for i, b := range *p { for _, b := range *p { if 'a' <= b && b <= 'z' { // (*p)[i] = b + 'A' - 'a' b = b + 'A' - 'a' } } fmt.Printf("%s\n", *p) // /usr/bin/tso } func main() { pathName := path("/usr/bin/tso") pathName.ToUpper() fmt.Printf("%s\n", pathName) // /usr/bin/tso } ``` ---- <div class="textleft"> ※ go 在 range 時所遍歷的值是複製的值而不是元素本身,而如果使用`&遍歷值`這樣拿到的也是臨時變數的位置 而範例二的重點我覺得是 range 的小細節,而非將重點放在 slice 上 </div> --- ## 延伸閱讀 - slice 相關豆知識 ---- ### 空 slice = nil 當你 `make` 一個空 slice 時 ```go sliceHeader{ Length: 0, Capacity: 0, ZerothElement: nil, } ``` ---- ```go var a []int fmt.Println(a == nil) // true b := make([]int,0) fmt.Println(b == nil) // flase ``` ---- ### String 也是 slice > Strings are actually very simple: they are just read-only slices of bytes with a bit of extra syntactic support from the language. ---- 所以你也可以直接做互相轉換 ```go str := string(slice) // reverse slice := []byte(usr) ``` ---- <div class="textleft"> 這種 slice-like 的設計,可以輕鬆的建立子字串,並且跟改後不會影響父子串 </div> ```go a := "1234567890" b := a[1:5] fmt.Println(b) // 12345 b = "54321" fmt.Println(a) // 1234567890 fmt.Println(b) // 54321 ```
{"metaMigratedAt":"2023-06-14T17:10:27.304Z","metaMigratedFrom":"Content","title":"Go Slices Slide","breaks":true,"contributors":"[{\"id\":\"69ade472-3ed3-499d-8a69-767243a31621\",\"add\":17682,\"del\":1384}]"}
    614 views