go 接口 构造器_Go 中接口值的复制
我一直在思考 Go 語言它是如何工作的。直到最近我才發現 Go 中一切都是基于值的。當我們向函數傳遞參數、迭代切片、執行類型斷言時我們都可以看到這一現象。在這些例子中,這些數據結構所存儲的值的拷貝會被返回。當我剛開始學習 Go 的時候,我對于這種實現方式很失望,但漸漸地我開始意識到這樣做對于我們的代碼來說有它的合理性。
我開始在想,如果我創建了一個所存儲的是值而非指針的接口類型的拷貝會發生什么。那么這個新的接口值會擁有自己新的副本值,還是共享原來的值?為了驗證我的猜想,我寫了一個小程序來檢查接口值。
https://play.golang.org/p/KXvtpd9_29
清單 1
06 package main 07 08 import ( 09 "fmt" 10 "unsafe" 11 ) 12 13 // notifier provides support for notifying events. 14 type notifier interface { 15 notify() 16 } 17 18 // user represents a user in the system. 19 type user struct{ 20 name string 21 } 22 23 // notify implements the notifier interface. 24 func (u user) notify() { 25 fmt.Println("Alert", u.name) 26 }在清單 1 的代碼中第 14 行,我們聲明了一個具有 notify 方法的接口類型 notifier。在 19 行我們聲明了一個 user 類型,這個類型在 24 行實現了 notifier 接口中的 notify 方法。現在我們擁有了一個接口類型和一個實現類型。
清單 2
28 // inspect allows us to look at the value stored 29 // inside the interface value. 30 func inspect(n *notifier, u *user) { 31 Word := uintptr(unsafe.Pointer(n)) + uintptr(unsafe.Sizeof(&u)) 32 value := (**user)(unsafe.Pointer(word)) 33 fmt.Printf("Addr User: %p Word Value: %p Ptr Value: %vn", u, *value, **value) 34 }在清單 2 中我們在 30 行寫了一個檢查的函數。這個函數返回給我們一個指向接口值的第二個字的指針。通過這個指針我們可以檢查此接口第二個字的值,以及第二個字指向的 user 值。我們需要審查這些值來真正理解接口的機制。
清單 3:
36 func main() { 37 38 // Create a notifier interface and concrete type value. 39 var n1 notifier 40 u := user{"bill"} 41 42 // Store a copy of the user value inside the notifier 43 // interface value. 44 n1 = u 45 46 // We see the interface has its own copy. 47 // Addr User: 0x1040a120 Word Value: 0x10427f70 Ptr Value: {bill} 48 inspect(&n1, &u) 49 50 // Make a copy of the interface value. 51 n2 := n1 52 53 // We see the interface is sharing the same value stored in 54 // the n1 interface value. 55 // Addr User: 0x1040a120 Word Value: 0x10427f70 Ptr Value: {bill} 56 inspect(&n2, &u) 57 58 // Store a copy of the user address value inside the 59 // notifier interface value. 60 n1 = &u 61 62 // We see the interface is sharing the u variables value 63 // directly. There is no copy. 64 // Addr User: 0x1040a120 Word Value: 0x1040a120 Ptr Value: {bill} 65 inspect(&n1, &u) 66 }清單 3 展示了從 36 行開始的主函數。 我們做的第一件事情就是在 39 行聲明了類型為 notifier 名為 n1 的接口變量,將其設為初始零值。然后在 40 行我們聲明了一個類型為 user 的變量 u,其初始值為 bill。
我們想要在將具體 user 值分配給接口之后,檢查這個接口值的第二個字包含什么。
清單 4
42 // Store a copy of the user value inside the notifier 43 // interface value. 44 n1 = u 45 46 // We see the interface has its own copy. 47 // Addr User: 0x1040a120 Word Value: 0x10427f70 Ptr Value: {bill} 48 inspect(&n1, &u)插圖 1:當我們將 user 的值分配給該接口之后,這個接口內部結構是什么樣的。
插圖 1 向我們展示了在分配后接口的內部結構視圖。我們可以看到這個接口有它自己的 user 值的拷貝。存儲在接口值內部的 user 值的地址和原始 user 值的地址并不相同。
我編寫這段代碼用來了解如果我創建了一個分配了值而非指針的接口值的副本會發生什么。新的接口值是否也有自己的副本,或者值是否會共享。
清單 5
50 // Make a copy of the interface value. 51 n2 := n1 52 53 // We see the interface is sharing the same value stored in 54 // the n1 interface value. 55 // Addr User: 0x1040a120 Word Value: 0x10427f70 Ptr Value: {bill} 56 inspect(&n2, &u)插圖 2:當接口值被拷貝后,這兩個接口值的內部構造。
插圖 2 給了我們答案。當我們創建一個接口值的拷貝時,這就是我們復制的全部內容。原來存儲在 n1 中的 user 值此時也共享給了 n2。每個接口值并沒有獲得它們獨有的拷貝。它們共享了同一個 user 值。
如果我們分配的是 user 值的地址而不是該值本身,那么所有這些情況都會有所改變。
清單 6
58 // Store a copy of the user address value inside the 59 // notifier interface value. 60 n1 = &u 61 62 // We see the interface is sharing the u variables value 63 // directly. There is no copy. 64 // Addr User: 0x1040a120 Word Value: 0x1040a120 Ptr Value: {bill} 65 inspect(&n1, &u)插圖 3: 當我們為接口分配了一個地址時,該接口值的內部構造。
在插圖 3 中,我們看到該接口現在指向的是被變量 u 引用的 user 值。 變量 u 的地址已經存儲到這個接口值的第二個字當中。這就意味著和 user 值相關的任何變化都會影響到這個接口值。
結論
我允許自己對 Go 在創建一個接口值的拷貝時,只存儲值而不是指針的情況感到困惑。有那么一瞬間,我想知道接口值的每個副本是否也創建了接口引用的值的副本。因為我們“存儲”了一個值而不是一個指針。但我們現在學到的是,由于地址始終會被存儲起來,因此它是被復制的地址,而不是值的本身。
via:Go 中接口值的復制 - Go語言中文網 - Golang中文社區
作者:William Kennedy 譯者:barryz 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
總結
以上是生活随笔為你收集整理的go 接口 构造器_Go 中接口值的复制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang 升级到新版本_Scikit
- 下一篇: java ssl 无证书_java –