亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

通過引用傳遞自定義切片類型

通過引用傳遞自定義切片類型

Go
忽然笑 2021-06-04 18:09:48
我對指針、切片和接口在 Go 中的交互方式感到困惑。這是我目前編碼的內容:type Loader interface {  Load(string, string)}type Foo struct {  a, b string}type FooList []Foofunc (l FooList) Load(a, b string) {  l = append(l, Foo{a, b})  // l contains 1 Foo here}func Load(list Loader) {  list.Load("1", "2")  // list is still nil here}鑒于此設置,我然后嘗試執行以下操作:var list FooListLoad(list)fmt.Println(list)但是,列表總是nil在這里。我的 FooList.Load 函數確實向l切片添加了一個元素,但僅此而已。將list在負載繼續nil。我想我應該能夠將引用傳遞給我的切片并附加到它。不過,我顯然缺少有關如何使其工作的內容。
查看完整描述

3 回答

?
慕哥9229398

TA貢獻1877條經驗 獲得超6個贊

我將簡化問題,以便更容易理解。那里正在做的事情與此非常相似,這也不起作用(您可以在此處運行):


type myInt int


func (a myInt) increment() { a = a + 1 }

func increment(b myInt)    { b.increment() }


func main() {

    var c myInt = 42

    increment(c)

    fmt.Println(c) // => 42

}

這不起作用的原因是因為 Go 按值傳遞參數,如文檔所述:


在函數調用中,函數值和參數按通常的順序計算。在對它們求值后,調用的參數按值傳遞給函數,被調用的函數開始執行。


實際上,這意味著上面示例中的每個a、b、 和c都指向不同的 int 變量,a并且b是初始c值的副本。


要修復它,我們必須使用指針,以便我們可以引用相同的內存區域(此處可運行):


type myInt int


func (a *myInt) increment() { *a = *a + 1 }

func increment(b *myInt)    { b.increment() }


func main() {

    var c myInt = 42

    increment(&c)

    fmt.Println(c) // => 43

}

Nowa和b都是包含變量地址的指針c,允許它們各自的邏輯改變原始值。注意記錄的行為仍持有此:a和b仍然是原始值的副本,但作為一個參數提供的原始值increment函數的地址的c。


切片的情況與此沒有什么不同。它們是引用,但引用本身是按值作為參數提供的,因此如果您更改引用,調用站點將不會觀察到更改,因為它們是不同的變量。


不過,還有一種不同的方法可以使其工作:實現一個類似于標準append函數的 API 。再次使用更簡單的示例,我們可以在increment不改變原始值且不使用指針的情況下通過返回更改后的值來實現:


func increment(i int) int {  return i+1 }

您可以在標準庫中的許多地方看到該技術,例如strconv.AppendInt函數。


查看完整回答
反對 回復 2021-06-28
?
隔江千里

TA貢獻1906條經驗 獲得超10個贊

Go 是按值傳遞的。對于參數和接收器都是如此。如果需要給切片賦值,則需要使用指針。


然后我在某處讀到你不應該將指針傳遞給切片,因為它們已經是引用


這并不完全正確,并且缺少故事的一部分。


當我們說某物是“引用類型”時,包括映射類型、通道類型等,我們的意思是它實際上是一個指向內部數據結構的指針。例如,您可以將地圖類型視為基本定義為:


// pseudocode

type map *SomeInternalMapStructure

所以要修改關聯數組的“內容”,不需要賦值給map變量;您可以按值傳遞映射變量,該函數可以更改映射變量指向的關聯數組的內容,并且它對調用者可見。當您意識到它是指向某個內部數據結構的指針時,這是有道理的。如果你想改變你只會分配給一個變量地圖這你想讓它指向內部的關聯數組。


但是,切片更復雜。它是一個指針(指向內部數組),加上長度和容量,兩個整數。所以基本上,你可以把它想象成:


// pseudocode

type slice struct {

    underlyingArray uintptr

    length int

    capacity int

}

所以它不“只是”一個指針。它是一個相對于底層數組的指針。但是長度和容量是切片類型的“值”部分。


因此,如果您只需要更改切片的元素,那么是的,它就像引用類型一樣,因為您可以按值傳遞切片并讓函數更改元素并且它對調用者可見。


但是,當您append()(這就是您在問題中所做的)時,情況就不一樣了。首先,append 影響切片的長度,而長度是切片的直接部分之一,而不是在指針后面。其次,append 可能會產生不同的底層數組(如果原始底層數組的容量不夠,則分配一個新的);因此切片的數組指針部分也可能會更改。因此有必要改變切片值。(這就是為什么要append()返回一些東西。)從這個意義上說,它不能被視為引用類型,因為我們不僅僅是“改變它指向的東西”;我們直接改變切片。


查看完整回答
反對 回復 2021-06-28
  • 3 回答
  • 0 關注
  • 229 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號