The first deep pit of Go language-comparison of interface and nil
Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. # interface Introduction The Go language is known for its simplicity and ease of use, and its syntax is simple, and it takes only a short time for developers familiar with C++,java to master the basic usage of the go language. Interface is a very important feature that is provided in the go language. A interface can define one or more functions, such as the IO that comes with the system. The definition of Readwriter is as follows: "' Gotype Readwriter interface {Read (b []byte) (n int, err error) Write (b []byte) (n int, err error)} ' ' Any type as long as it provides a binding function implementation of Read and write, go considers this type to implement the interface (Duck-type), rather than Java requires the developer to use implements to indicate. However, the interface of the go language has a special pit feature in use, which is a problem that needs to be avoided when you compare the value of a interface type to nil. # a real stepping-hole this is one of the actual bugs that we encountered in the development of [Goworld Distributed Game Server] (Https://github.com/xiaonanln/goworld). Because Goworld supports a variety of different databases (including Mongodb,redis, etc.) to save the server object, Goworld provides a unified object storage interface definition on the upper level, while the different object database implementations only need to implement ' Entitystorage ' The functions provided by the interface. "' go//Entitystorage defines the interface of entity storage Backendstype Entitystorage Interface {List (TypeName string) ([]common. EntityId, error) Write (TypeName string, EntityId common. EntityId, Data interface{}) Errorread (TypeName string, EntityId common. EntityId) (interface{}, error) Exists (TypeName string, EntityId common. EntityId) (bool, errOR) Close () IsEOF (err error) bool} ' "As an example of an implementation using Redis as an object database, the function ' Openredis ' connects to the Redis database and eventually returns a ' Redisentitystorage ' The pointer to the object. "' go//Openredis opens Redis as entity storagefunc Openredis (URL string, Dbindex int) *redisentitystorage {c, err: = Redi S.dialurl (URL) if err! = Nil {return nil}if dbindex >= 0 {If _, err: = C.do ("select", Dbindex); Err! = nil {return nil}} es: = &redisentitystorage{c:c,}return es} "in the upper logic, we use the ' Openredis ' function to connect to the Redis database and return the ' Redisentitystorage ' The pointer assigns a ' entitystorage ' interface variable, because the ' Redisentitystorage ' object implements all functions defined by the ' Entitystorage ' interface. "' Govar storageengine storageengine//This is a global variable storageengine = Openredis (CFG). URL, Dbindex) if storageengine! = Nil {//connection succeeded ...} else {//connection failed ...} The code above looks normal, ' Openredis ' returns nil when the Redis database fails, and the caller compares the return value to nil to determine if the connection was successful. * * This is one of the few deep pits in the go language because it runs the logic of successful connection regardless of whether the ' Openredis ' function is connected to Redis. **# looking for the problem to understand the problem, you first need to understand the nature of the interface{} variable. In the go language, a variable of type interface{} contains 2 pointers, one pointer to the type of the value, and the other pointer to the actual value. We can use the following test code to verify. "' go//interfacestructure defines the internal structure of a interface{} type interfacestructure struct {pt uintptr//Pointer to value type PV uintptr//Pointer to value content}//asinterfacestructure convert a interface{} to Interf Acestructurefunc asinterfacestructure (i interface{}) interfacestructure {return * (*interfacestructure) (unsafe. Pointer (&i))}func testinterfacestructure (t *testing. T) {var i1, i2 interface{}var v1 int = 0x0aaaaaaaaaaaaaaavar V2 int = 0X0BBBBBBBBBBBBBBBI1 = V1i2 = v2fmt. Printf ("sizeof interface{} =%d\n", unsafe. Sizeof (I1)) fmt. Printf ("I1%x%+v\n", I1, Asinterfacestructure (I1)) fmt. Printf ("I2%x%+v\n", I2, Asinterfacestructure (I2)) var nilinterface interface{}fmt. The output of Printf ("Nil interface =%+v\n", Asinterfacestructure (Nilinterface))} "" is as follows: "' sizeof interface{} = 16i1 aaaaaaaaaaaaaaa {pt:5328736 pv:825741282816}i2 bbbbbbbbbbbbbbb {pt:5328736 Pv:825741282824}nil interface = {pt:0 pv:0} ' So for a nil variable of type interface{}, its two pointers are 0. This is in line with the standard definition of nil in the go language. In the go language, nil is ' 0 value ', whereas in languages such as Java, NULL is actually a ' null pointer '. There is no longer any difference between the zero value and the null pointer. When we assign a value of a specific type to a variable of type interface, the type and value areare assigned to the two pointers in the interface. If the value of this specific type is nil, the interface variable will still store the corresponding type pointer and value pointer. "' Gofunc testassigninterfacenil (t *testing. T) {var p *int = Nilvar i interface{} = pfmt. Printf ("%v%+v is nil%v\n", I, Asinterfacestructure (i), i = = nil)} "" is entered as follows: ' ' go<nil> {pt:5300576 pv:0} is nil false "Visible, in this case, although we assign a nil value to interface{}, but in fact the interface still has a pointer to the type, so take this interface variable to compare with the nil constant will return ' false '. # How to solve this problem to avoid the hole in the go language, all we have to do is avoid assigning a value to the interface variable of a specific type that might be nil. As an example of the above ' Openredis ', one method is to perform a non--nil check on the result returned by ' Openredis ', and then assign the value to the interface variable, as shown below. "' Govar storageengine storageengine//This is a global variable for redis: = Openredis (CFG). URL, dbindex) If Redis = nil {///connection succeeded Storageengine = redis//OK Redis is not nil then assign to interface variable} else {//connection failed ...} Another method is to have the ' Openredis ' function return the value of the Entitystorage interface type directly, so that the return value of ' Openredis ' can be correctly assigned to the Entitystorage interface variable directly. "' go//Openredis opens Redis as entity storagefunc Openredis (URL string, Dbindex int) Entitystorage {c, err: = Redis. Dialurl (URL) if err! = Nil {return nil}if dbindex >= 0 {If _, err: = C.do ("SeleCT ", Dbindex); Err! = Nil {return Nil}}es: = &redisentitystorage{c:c,}return es} "" As for that method better, it is a matter of opinion. Hope that everyone in the actual project do not step on the pit, even stepping can quickly jump out! > Open source Distributed Game Server engine: [Https://github.com/xiaonanln/goworld] (Https://github.com/xiaonanln/goworld), Welcome to star, learn together > > Friends interested in the development of the Go Language server Welcome to join QQ Group: [662182346] (http://shang.qq.com/wpa/qunwpa?idkey= F2A99BD9BD9E6DF3528174180AAD753D05B372A8828E1B8E5C1EC5DF42B301DB) 3,144 reads
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.