2015-03-19 06:42:12 8 Comments
I fail to understand how to correctly assure that something is not nil
in this case:
package main
type shower interface {
getWater() []shower
}
type display struct {
SubDisplay *display
}
func (d display) getWater() []shower {
return []shower{display{}, d.SubDisplay}
}
func main() {
// SubDisplay will be initialized with null
s := display{}
// water := []shower{nil}
water := s.getWater()
for _, x := range water {
if x == nil {
panic("everything ok, nil found")
}
//first iteration display{} is not nil and will
//therefore work, on the second iteration
//x is nil, and getWater panics.
x.getWater()
}
}
The only way I found to check if that value is actually nil
is by using reflection.
Is this really wanted behaviour? Or do I fail to see some major mistake in my code?
Related Questions
Sponsored Content
1 Answered Questions
1 Answered Questions
[SOLVED] How to read slice in Reflect.FieldByName()
- 2018-05-14 07:33:24
- elham anari
- 90 View
- 0 Score
- 1 Answer
- Tags: go
1 Answered Questions
[SOLVED] Get Interface Field Values From an Interface Without Declaring Structure in Golang
- 2017-09-27 11:11:22
- Amandeep kaur
- 4788 View
- 2 Score
- 1 Answer
- Tags: go reflection interface
1 Answered Questions
0 Answered Questions
2 Answered Questions
[SOLVED] Checking of variable implements interface without compiling
- 2015-08-20 08:45:44
- wak
- 2292 View
- 3 Score
- 2 Answer
- Tags: reflection go interface
4 Answered Questions
[SOLVED] Go reflection with interface embedded in struct - how to detect "real" functions?
- 2015-05-01 14:20:57
- BadZen
- 1000 View
- 5 Score
- 4 Answer
- Tags: reflection interface go embedding
1 Answered Questions
[SOLVED] Get struct value from interface
- 2015-04-18 03:21:54
- RockyMountainHigh
- 1850 View
- 1 Score
- 1 Answer
- Tags: go
1 Answered Questions
[SOLVED] Why does putting a pointer in an interface{} in Go cause reflect to lose the name of the type?
- 2015-02-05 07:41:07
- Nate
- 352 View
- 6 Score
- 1 Answer
- Tags: pointers reflection go
2 comments
@icza 2015-03-19 06:47:48
The problem here is that
shower
is aninterface
type. Interface types in Go hold the actual value and its dynamic type. More details about this: The Laws of Reflection #The representation of an interface.The slice you return contains 2 non-
nil
values. The 2nd value is an interface value, a (value;type) pair holding anil
value and a*display
type. Quoting from the Go Language Specification: Comparison operators:So if you compare it to
nil
, it will befalse
. If you compare it to an interface value representing the pair(nil;*display)
, it will betrue
:This seems unfeasible as you'd have to know the actual type the interface holds.
Why is it implemented this way?
Interfaces unlike other concrete types (non-interfaces) can hold values of different concrete types (different static types). The runtime needs to know the dynamic or runtime-type of the value stored in a variable of interface type.
An
interface
is just a method set, any type implements it if the same methods are part of the method set of the type. There are types which cannot benil
, for example astruct
or a custom type withint
as its underlying type. In these cases you would not need to be able to store anil
value of that specific type.But any type also includes concrete types where
nil
is a valid value (e.g. slices, maps, channels, all pointer types), so in order to store the value at runtime that satisfies the interface it is reasonable to support storingnil
inside the interface. But besides thenil
inside the interface we must store its dynamic type as thenil
value does not carry such information. The alternate option would be to usenil
as the interface value itself when the value to be stored in it isnil
, but this solution is insufficient as it would lose the dynamic type information.In general if you want to indicate
nil
for a value ofinterface
type, use explicitnil
value and then you can test fornil
equality. The most common example is the built-inerror
type which is an interface with one method. Whenever there is no error, you explicitly set or return the valuenil
and not the value of some concrete (non-interface) type error variable (which would be really bad practice, see demonstration below).In your example the confusion arises from the facts that:
shower
)shower
but a concrete typeSo when you put a
*display
type into theshower
slice, an interface value will be created, which is a pair of (value;type) where value isnil
and type is*display
. The value inside the pair will benil
, not the interface value itself. If you would put anil
value into the slice, then the interface value itself would benil
and a conditionx == nil
would betrue
.Demonstration
See this example: Playground
Output:
In case 2 a
nil
pointer is returned but first it is converted to an interface type (error
) so an interface value is created which holds anil
value and the type*MyErr
, so the interface value is notnil
.@sharpner 2015-03-19 06:52:11
that's a pretty... big fail isn't it? How can I write a library that provides interfaces that way? I could assure that it works correctly if the users provides correct types with nil value. I guess if x == (shower)(nil) would make sense but that is just shocking..
@Volker 2015-03-19 07:06:06
There is nothing shocking about it. Your return a slice of two interface values, both are non-nil. One of those non-nil interface values contains a nil value. The interface value has to be non-nil to contain anything, even a nil. You can either fix it like icza suggested, or redesign your API, e.g. not returning a slice of interfaces.
@icza 2015-03-19 07:11:27
@sharpner You can provide a library which uses/returns values of interface types. But if the value is "missing", return
nil
explicitly.@sharpner 2015-03-19 07:42:35
I'm still trying to wrap my head around how to redesign the api. Maybe a typedef something []shower will get me further or I don't know yet. But I really learned something quite new about go today. My example simplifies the real problem somewhat. In the final implementation, the user of my library has to implement getWater() and therefore can make mistakes. So, since I got to assume that this will happen, I needed to assure that no nil values are in the slice. I anticipated that users can return pointer to structs or structs that implemented the interface....
@Nate Finch 2015-03-19 17:59:38
A nil pointer is not an invalid value. Note that you can call methods on nil pointers just fine: play.golang.org/p/Agd8eIwaKZ Try reading my post about nil pointer and nil interfaces, it may help: npf.io/2014/05/intro-to-go-interfaces/#toc_4
@sharpner 2015-05-07 11:13:15
wow that's interesting. Thanks @NateFinch
@Target-san 2015-11-10 09:56:22
@icza I understand that this topic is a bit old. But I can't really understand why this is allowed as implicit coercion. Wouldn't it be more understandable to produce fully nil interface pointer from nil structure pointer? Or to check only value pointer against nil? Pike et al were saying so much about removing all unobvious and controversial things. And yet they leave this pretty much bug. Because it effectively allows to produce reference with broken invariants. Calling methods over nil pointers is such a rare case, and more like a hack, that I don't consider it as a valid explanation.
@AndrewN 2015-11-11 00:54:32
@Target-san In that case a coder could no long check an interface variable against
nil
to verify that it was truly completelynil
, that it held no dynamic type information, and you'd simply move this entire class of tricky behavior in a slightly different problem space. IOW, there's no simple/obvious answer here, because sometimes you might want to know if the value is nil and sometimes you might want to know if both type/value are nil.@Target-san 2015-11-12 09:42:55
@AndrewN Thanks for explanation. Though I found the answer on go-nuts group. Brief: Go uses pointers, and not references. For reference,
nil
is likeNaN
for floats. But for pointernil
is just yet another value. The difference seems to be semantical, because references and pointers usually have same representation under the hood.@AndrewN 2015-11-12 16:12:23
Yeah I read that thread, it was quite educational. :)
@Moshe Revah 2015-12-17 20:09:51
Let's think of an interface as a pointer.
Say you have a pointer
a
and it's nil, pointing to nothing.Then you have a pointer
b
and it's pointing toa
.See what happened?
b
points to a pointer that points to nothing. So even if it's a nil pointer at the end of the chain,b
does point to something - it isn't nil.If you'd peek at the process' memory, it might look like this:
See?
a
is pointing to address 0 (which means it'snil
), andb
is pointing to the address ofa
(1000000).The same applies to interfaces (except that they look a bit different in memory).
Like a pointer, an interface pointing to a nil pointer would not be nil itself.
Here, see for yourself how this works with pointers and how it works with interfaces.