Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Returned interface is never nil? #42663

Closed
ivanjaros opened this issue Nov 17, 2020 · 13 comments
Closed

Returned interface is never nil? #42663

ivanjaros opened this issue Nov 17, 2020 · 13 comments

Comments

@ivanjaros
Copy link

ivanjaros commented Nov 17, 2020

What version of Go are you using (go version)?

go1.15 windows/amd64

What did you do?

I am returning exported interface from a method/function and when I do check if it is nil, it never is.
If i return pointer to a struct matching the interface instead of interface, the nil check works as expected.
This is inconsistent, for example with error interface.

Example: https://play.golang.org/p/ghzg8n1fx_y

In my code, when I do:

if r := something(); r != nil {
... // here i can print the r and it will print as nil so it shouldn't have passed the if check
}

What did you expect to see?

If I return nil exported interface I would expect nil check to work properly.

What did you see instead?

Nil check did not work properly when nil is returned from a function as exported interface.

@ALTree
Copy link
Member

ALTree commented Nov 17, 2020

See https://golang.org/doc/faq#nil_error.

Similar situations to those described here can arise whenever interfaces are used. Just keep in mind that if any concrete value has been stored in the interface, the interface will not be nil. For more information, see The Laws of Reflection.

@ALTree ALTree closed this as completed Nov 17, 2020
@ivanjaros
Copy link
Author

ivanjaros commented Nov 17, 2020

i don't get it. when the return value is error interface and i return nil the returned "value" is interface of type error with nil value and it will be properly handled via nil check. just as i put into my example code. when i did the same with my code, different interface, nil returned just the same, the nil check did not work properly. so what am i missing here?

for error the result was {T=error, V = nil}, for my interface(io.ReadCloser) the result was {T=io.ReadCloser, V = nil} yet they behave differently for nil check.

@ALTree
Copy link
Member

ALTree commented Nov 17, 2020

Ok, but we don't use the issue tracker for questions about the language. please see the Questions wiki page.

@ivanjaros
Copy link
Author

ivanjaros commented Nov 17, 2020

Well then consider it a bug since two identical uses produce two different results = unexpected behavior.

@ngopher
Copy link

ngopher commented Nov 17, 2020

@ivanjaros
Hi.
What do you expect as the result of your code snippet?
Please tell me that, I saw your code and there are no unexpected behavior.

@ivanjaros
Copy link
Author

ivanjaros commented Nov 17, 2020

how's nil passing !=nil check EXPECTED behavior????

if I do:


func (msg *Message) A() io.ReadCloser {
	if msg.htmlBody == nil {
	  return nil
	}
	return msg.htmlBody
}

the nil check is fine. so for some reason the nil pointer to the nopCloser will not be considered nil if "typed" into io.ReadCloser, which is just baffling. it is one thing to detect the interface(ie. type system) but either way the nil check simply cannot pass. that's just plain wrong and such a massive bug that I can't even...

..and i am not complaining about passing nil pointers/interfaces/... I am complaining about the fact that nil check does not behave in expected way. ie. nil pointer will work as expected but interface with nil pointer inside will not. that's just...insane. it's like the opposite behavior where pointer wraps object and its matching interface, which works with nil check, but interface(pointer in itself) wrapping object(nil) does not. that's is simply unacceptable.

@ngopher
Copy link

ngopher commented Nov 17, 2020

@ivanjaros
As you recognized by yourself, the interface in nil only when that doesn't contain an underlying concrete type, or some method signatures.
In your case, you return the io.ReadCloser which isn't nil, although you return a nil pointer, but every where you use that, actually you are using the io.ReadCloser interface, not the pointer you returned from method.
I am agree with you, it is confusing, but you may complain @robpike 😁

@ivanjaros
Copy link
Author

ivanjaros commented Nov 17, 2020

yeah, i get it, but it's just wrong. i mean, ... lets take the fact that nil can match the interface, which is the sole reason you can return it. the returned value of nil will be the interface with nil value wrapped inside of it MyInterface{V=nil} and nil check will work. that is why the error im my example works as expected.

When my pointer p{T=nopCloser, V=nil} is considered nil under all circumstances and I return it as the interface then the returned value will be MyInterface{V=p}. So in this case the nil check will fail because the pointer inside the interface has a type(nopCloser) EVEN THOUGH THE POINTER WILL ALWAYS BE CONSIDERED NIL. This is what my issue here is. It feels like the compiler is lacking one additional for loop to check if the wrapped value inside the interface can be considered nil or not and not just only check if the wrapped value is actually nil itself. This is lazy programming in my opinion.

I am agree with you, it is confusing, but you may complain @robpike 😁

It took me some time to "get" Go but now I do and I adore its simplicity and applaud its creators(like Rob). But this can't be overlooked, imho.

@ALTree
Copy link
Member

ALTree commented Nov 17, 2020

The compiler is behaving according to the specification. You're not the first person to be surprised by this (that's why it's in the FAQs page), but once you understand how interfaces work under the hood the outcome makes sense. The way this works hasn't changed in the last 10 years, and it seems unlikely to me it'll be changed now just because you're ranting in caps about it. Changing this wouldn't even be backward compatible, I believe. So let's conclude the discussion here.

@ivanjaros
Copy link
Author

Yes, obviously a person never achieves anything in here. But maybe the Go team could look into fixing some architectural language issues instead of worrying so much about backwards compatibility. Tough love is sometimes needed. I get it but if everything is written in stone how are we able to move forward? Just adding new features doesn't sound smart.

@ianlancetaylor
Copy link
Contributor

There have been various attempts to address this over the years. For example, #22729. But we can't change the current behavior, as that would break existing working programs.

In any case, as others have said, this issue is the wrong place to discuss this. The right place is golang-nuts or some other forum. Please respect our attempts to guide discussions in ways that make them more useful. Thanks.

@ivanjaros
Copy link
Author

Sure, np. It is what it is ...maybe one last question though - how would this break backwards compatibility?

@ianlancetaylor
Copy link
Contributor

Please ask on a forum. See https://golang.org/wiki/Questions. Thanks.

@golang golang locked and limited conversation to collaborators Nov 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants