2017-02-04 134 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因此具有指针至约类型存储并指向数据本身的信息。它的类型和值信息