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

Allocless way to check interface compatibility #33

Open
vbauerster opened this issue Sep 9, 2019 · 4 comments
Open

Allocless way to check interface compatibility #33

vbauerster opened this issue Sep 9, 2019 · 4 comments

Comments

@vbauerster
Copy link

vbauerster commented Sep 9, 2019

Interfaces part shows a way to check interface compatibility (fulfillment):

type NullWriter struct {}
var _ io.Writer = &NullWriter{}

Nothing wrong with above code, but for completeness sake it worth mention that we needn't allocate a new variable since any value of type *NullWriter will do, even nil:

type NullWriter struct {}
var _ io.Writer = (*NullWriter)(nil)

This explicit conversion works, because nil is typed in Go. Check this playground example.

@vbauerster vbauerster changed the title No need to allocate to check interface compatibility Allocless way to check interface compatibility Sep 10, 2019
@Pungyeon
Copy link
Owner

Pungyeon commented Dec 6, 2019

I see! Thank you so much for taking the time to point this out! 🙏 I actually thought that the compiler would optimise the _ variable away, but I suppose not?

I will write up a pull request for this 😄 Sorry for the late reply!

@Pungyeon
Copy link
Owner

Pungyeon commented Dec 7, 2019

@vbauerster I had a look at this, using the build flag go build -gcflags '-m -m' ./main.go on the following code:

package main

import (
	"io"
)

type NullWriter struct{}

func (writer *NullWriter) Write(b []byte) (int, error) {
	return 0, nil
}

var _ io.Writer = (*NullWriter)(nil)
var _ io.Writer = &NullWriter{}

func main() {
	var _ io.Writer = &NullWriter{}
	var _ io.Writer = (*NullWriter)(nil)
	return
}

This gave me the following output:

# command-line-arguments
./main.go:9:6: can inline (*NullWriter).Write as: method(*NullWriter) func([]byte) (int, error) { return 0, nil }
./main.go:16:6: can inline main as: func() { var _ io.Writer; _ = &NullWriter literal; var _ io.Writer; _ = (*NullWriter)(nil); return  }
/var/folders/yk/2tnyv8g550zb0bg4vt0vn4l40000gn/T/go-build491024858/b001/_gomod_.go:6:6: can inline init.0 as: func() { keepalive_modinfo = __debug_modinfo__ }
./main.go:9:7: (*NullWriter).Write writer does not escape
./main.go:9:33: (*NullWriter).Write b does not escape
./main.go:17:20: main &NullWriter literal does not escape
./main.go:17:6: main &NullWriter literal does not escape
./main.go:18:6: main (*NullWriter)(nil) does not escape

This suggests that the compiler actually optimises the _ variables away. The output suggests that neither line 17 and 18 do not get allocated on the heap, and it seems to have completely ignored lines 13 and 14. So, I believe that there is no difference between the two methods?

Perhaps I am going about this all wrong though? If you have input on this, let me know, I would love to learn!

Thank you 🙏

@vbauerster
Copy link
Author

vbauerster commented Dec 8, 2019

Frankly speaking I didn't think about _ part. But is sounds plausible that there is no difference because of compiler optimisation. So now I'm not sure if it worth mentioning the other way at all. 🤔

@Pungyeon
Copy link
Owner

Pungyeon commented Dec 9, 2019

Alright, I will keep the issue open, so that anyone can chime in and maybe give us a definitive answer on this 😄 Thanks again for opening the issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants