golang slice在其他函數中修改,易錯點

回憶起寫golang時候踩過的坑,那是我逝去的青春……

我們可能會遇到給其他函數傳遞一個slice,讓其他函數給這個slice做一些修改的情況。想到slice是引用傳遞,可以直接傳遞slice用作修改,於是可能出現下面這種情況:

package main

import (
	"fmt"
	"testing"
)

func TestModifySlice(t *testing.T) {
	s := []string{"123"}
	modifySlice(s)
	fmt.Println(s)
}
func modifySlice(s []string) {
	s[0] = "modify:123"
	s = append(s, "456")
}

我們利用modifySlice函數對傳入的s進行修改和添加的操作,想到slice是引用傳遞,在modifySlice中的修改必然會生效。

但是實際上呢,我們看一下運行情況:

運行結果表明我們對s[0]做的修改生效了,但是添加新元素的操作卻未生效,這是爲什麼呢?

原因就在於添加新元素是用的append,並將原先的引用重新賦值了!

我們把引用當成指針來看,指針指向的內容就是slice的數據內容。

指針本身是一種變量類型,指針類型在傳遞時,是值傳遞!

也就是說我們在外面調用modifySlice時,傳入s是一個指針變量,當進入到modifySlice函數時,形參s‘跟外面傳入的s並不是一個值,是s的值拷貝!但是他們的值是一樣的,即指向的數據內容都是slice中的數據。

我們用s[0] = ”modify:123“做了修改,實際上是修改指針指向的數據的內容!這裏肯定會生效!

然而我們用這種方式:s = append(s, "456"),這是在修改指針,並不是在修改指針指向的數據!

append可能會因爲s的cap不足,重新分配空間,所以append有一個返回參數。

我們對modifySlice中的s進行了修改,但是s是指針,僅僅是外層傳入的slice指針的拷貝,說明我們並沒有對外層的slice重新賦值!

這就是添加新元素失敗的原因。

 

如何改呢?

我們要對指針進行修改,指針是值傳遞,若想修改,自然要傳遞指針的指針啦!

於是我們可以修改成這樣:

package main

import (
	"fmt"
	"testing"
)

func TestModifySlice(t *testing.T) {
	s := []string{"123"}
	modifySlice(&s)
	fmt.Println(s)
}
func modifySlice(s *[]string) {
	(*s)[0] = "modify:123"
	*s = append(*s, "456")
}

再來看運行結果:

完美解決了我們在上面遇到的問題~~

等等,是不是感覺寫法很難看?golang的特點是簡單易懂,這種寫法是c、c++式寫法,各種取地址、解引用,看起來很高端,但是理解起來要稍微轉個彎。

golang一般不會對傳入的參數進行修改,如果修改,以返回值的形式返回修改的內容。golang式寫法是簡單易懂的代表,寫法也很優美,直接將slice作爲返回值返回:

package main

import (
	"fmt"
	"testing"
)

func TestModifySlice(t *testing.T) {
	s := []string{"123"}
	s = modifySlice(s)
	fmt.Println(s)
}
func modifySlice(s []string) []string {
	s[0] = "modify:123"
	s = append(s, "456")
	return s
}

結果毫無疑問也是正確的。

現在想下,如果一開始就用golang式寫法,是不是就不會掉進這個坑裏了?答案是肯定的,這就說明代碼的風格與規範也是我們應該提高的一項技能呀……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章