2017-02-04 139 views
1

我有兩種變量。檢查Go playground,我不明白爲什麼會發生這種情況。問題:我從Models得到的應該是struct用於GORM First()函數。golang指針之間的區別

的代碼:

package main 

import (
    "fmt" 
) 

type Test struct { 
    Test string 
} 

var Models = map[string]interface{}{ 
    "test": newTest(), 
} 

func main() { 
    test1 := Test{} 
    fmt.Println("Test 1: ") 
    fmt.Printf("%v", test1) 
    fmt.Println() 
    fmt.Println("Test 1 as pointer: ") 
    fmt.Printf("%v", &test1) 
    fmt.Println() 
    test2 := Models["test"] 
    fmt.Println("Test 2: ") 
    fmt.Printf("%v", test2) 
    fmt.Println() 
    fmt.Println("Test 2 as pointer: ") 
    fmt.Printf("%v", &test2) 

} 

func newTest() Test { 
    var model Test 
    return model 
} 
+0

你應該在這裏粘貼代碼。簡而言之,'test1'具有'Test'類型,'test2'具有'interface {}'類型(a.k.a空接口)。這是一種不同的類型。 – ymonad

+0

@ymonad是的,我檢查了它,但我怎樣才能將它返回到正確的結構? – PumpkinSeed

+0

當然,您可以將'map [string] interface {}'更改爲'map [string] Test'。不過,我想這是[XY問題](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)。你有沒有想要解決的具體問題? – ymonad

回答

1

TL; DR:在你傳遞*Test類型的值用於打印的第一種情況,但是在第二種情況下你傳遞*interface{}類型的值! %v動詞表示使用默認格式設置格式,但默認格式取決於值的類型。


你看到的區別只是默認格式化fmt包的實施細則。

您使用fmt.Printf()

func Printf(format string, a ...interface{}) (n int, err error) 

這需要一個格式字符串和其他參數作爲interface{}類型。因此,請注意,如果您傳遞的值不是interface{}類型的值,則該值將被包裝爲類型爲interface{}的值。

現在讓我們看看你的例子:

test1 := Test{} 
// ... 
fmt.Printf("%v", &test1) 

test1Test型的,和你通過&test1這是*Test類型。這將被包裹在一個interface{}。從fmt封裝文檔的格式規則:

對於化合物目的,元件使用這些規則印刷,遞歸地,佈置是這樣的:

struct:    {field0 field1 ...} 
array, slice:  [elem0 elem1 ...] 
maps:    map[key1:value1 key2:value2] 
pointer to above: &{}, &[], &map[] 

既然是一個指針,指向一個struct,將使用&{}格式。 Test的字段爲Test string,但未設置其值,因此它默認爲類型爲stringzero value,這是空字符串""。這就是爲什麼你顯示時什麼也看不到。請注意,如果你會喜歡這個初始化它:

test1 := Test{"a"} 

輸出本來:

&{a} 

讓我們看看你的第二個例子:

test2 := Models["test"] 
// ... 
fmt.Printf("%v", &test2) 

第一行是short variable declarationtest2的類型將從右邊的表達式中推斷出來。右側表達式是index expression,爲地圖編制索引。其類型將是地圖的值類型,並且由於Models的類型爲map[string]interface{},因此test2的類型將爲interface{}

到目前爲止這麼好。但是當你試圖像fmt.Printf("%v", &test2)那樣打印時會發生什麼?你傳遞一個指向test2的指針,它的類型爲interface{},所以你通過的是*interface{},因爲這與interface{}不一樣,所以它會被包裝在另一個interface{}的值中。

那麼傳遞給fmt.Printf()的值是interface{}的值,將*interface{}值作爲test2變量的地址。

而現在,在這裏適用的格式規則:

爲%V的默認格式爲:

bool:     %t 
int, int8 etc.:   %d 
uint, uint8 etc.:  %d, %x if printed with %#v 
float32, complex64, etc: %g 
string:     %s 
chan:     %p 
pointer:     %p 

由於要格式化的值是一個指針(*interface{}),%v將默認爲%p,即:

Pointe R:

%p base 16 notation, with leading 0x 

所以結果是正確打印十六進制格式的地址值,例如:

0x1040a160 

test2獲得一個結構,你可以使用type assertion。所以它應該是這樣的:

t2 := Models["test"] 
test2 := t2.(Test) // test2 is of type Test 

test2具有相同類型到的test1,並在打印時會產生相同的結果。試試Go Playground

最佳將是雖然存儲*Test值在地圖,所以沒有類型斷言或偶存儲在局部變量將是必要的,因爲存儲在地圖的interface{}將已經是指向Test,其可用於/按照原樣傳遞。

+0

很好的答案,大部分我都發現了這一點,但目前我沒有解決我的問題:我寫了我的問題高於你的建議? _「Models數組充滿了結構,而我alwys想要返回正確結構的實例。例如,有300個結構,它們都代表數據庫表,我總是想返回表的一個實例,我想使用「_ – PumpkinSeed

+0

@ PumpkinSeed我不明白你的例子,也許你應該將其作爲一個新問題發佈? – icza

+0

我採取了它,我希望一切都可以理解:http://stackoverflow.com/questions/42038782/golang-database-manager-api-concept-error-with-type-assertion – PumpkinSeed

1

按照尺寸和對準保證按照golang文檔

結構或陣列型具有大小零,如果它不包含任何字段(或 元件,分別)具有大於零的大小。兩個 不同的零大小變量可能在內存中具有相同的地址。

這就是爲什麼Test 1&{}

接口值被表示爲一個雙字對賦予指針 到關於存儲在接口的類型和一個指向 相關聯的數據的信息。

Test2interface{},的Test2因此具有指針至約類型存儲並指向數據本身的信息。它的類型和值信息