Accepting an argument that satisfies a local package interface from outside the package in GO

Lets say you’re writing some semi-complex code… For example you’re writing an engine for a network service and you want to allow people to wrap transports around it however they want. So maybe you want someone to be able to slap this functionality inside a websockets server, or a zmq workflow, or an RPC server, or communicate via some other heretofore unknown or unconsidered method. So you define an interface for all the functions that you need to make the engine work… but how do you accept mything.MyNetworkInterface when the actual type could be anything?

I actually beat my head on this for long enough that it turned out to be slightly embarrassing. The answer was actually in a set of articles (here, and here) that I’d already read (and if you’re here looking for the same thing that I was looking for you’ve undoubtedly already read them as well… but it hasn’t clicked yet…)

So let me give you an example that hopefully illustrates exactly how you do this…

First we have our package: “foo” which defined the “blahable” interface like so…

type blahable interface{
	Blah()
}

Next we have our program we’re writing… and we’ve written our struct “bar”, a pointer to which will satisfy the foo.blahable interface because it impliments the Blah() method like so…

type bar struct {
	count int
}

func (b *bar)Blah() {
	b.count = b.count + 1
	log.Printf("Blah() #%d", b.count)
}

Now the thing that I was having a hard time with was that I wanted to write a foo.DoBlah() function which takes a foo.blahable  and uses the Blah() method. Obviously this is simplified and contrived, but bare with me here…

I was trying things like this

func DoBlah(arg blahable) {
    arg.Blah()
}

Or this

func DoBlah(arg interface{}) {
    arg.Blah()
}

Or a dozen other things (with permutations of pointers, not pointers, etc)… Did I mention that I fumbled with this for an embarrassingly long time?  I would end up with maddening errors about how this could not be passed as that because it’s the wrong type; or how this was not an interface of type that; etc. Of course trying out pointers and non pointers and references out of frustration only added fuel to the fire of my confusion…

Also frustratingly most of the questions and answers out there are for types within the same package. I wanted things unknown to the current package to be passed in and used as an interface defined locally inside the package.

First let me give you a foo package and a main package that actually do what I want.

package foo

import "log"

type blahable interface{
	Blah()
}

func DoBlah(blah interface{}) {
	log.Printf( "entering foo.blah()..." )
	theblah := blah.(blahable)
	theblah.Blah()
	theblah.Blah()
	theblah.Blah()
	theblah.Blah()
	log.Printf( "leaving foo.blah()..." )
}
package main

import "foo"
import "log"

type bar struct {
	count int
}

func (b *bar)Blah() {
	b.count = b.count + 1
	log.Printf("Blah() #%d", b.count)
}

func main() {
	mybar := new(bar)
	mybar.Blah()
	mybar.Blah()
	foo.DoBlah(mybar)
	mybar.Blah()
	mybar.Blah()
}

Which outputs the following…

 2013/08/18 21:05:21 Blah() #1
 2013/08/18 21:05:21 Blah() #2
 2013/08/18 21:05:21 entering foo.blah()...
 2013/08/18 21:05:21 Blah() #3
 2013/08/18 21:05:21 Blah() #4
 2013/08/18 21:05:21 Blah() #5
 2013/08/18 21:05:21 Blah() #6
 2013/08/18 21:05:21 leaving foo.blah()...
 2013/08/18 21:05:21 Blah() #7
 2013/08/18 21:05:21 Blah() #8

So here’s the deal in two points…

#1 You can accept any type as an “empty interface” (interface{}) because every type has at least no methods and no members. But you can’t use it as is after it being passed to you because of the same reason that you can accept it: it has no defined methods or members. Which is why in the foo package inside func DoBlah() we must assert blah (our passed empty interface) to an interface of type blahable via the following.

theblah := blah.(blahable)

Had the passed interface not satisfied blahable a runtime panic would have happened… (though it’s catchable if you want)

#2 A type and a pointer to a type are different things and can satisfy, or not, an interface separately… Given the “blahable” interface from above and the following code…

type A struct {}
func (a A) Blah() {}

Type B struct {}
func (b *B) Blah() {}

Type C struct{}

Obviously type C does not implement blahable.  Given foo = A{} foo implements blahable; but when given foo = new(A) foo (which is a pointer to an A) does not implement blahable.  The opposite  is true for B. foo = B{} does not implement blahable, but new(B), or &foo both do. (see the method signatures)

Perhaps inconveniently you can’t define both… at runtime you’ll get a method redeclared error…

When’s it going to rain?

The wife and I decided to be better about bringing in the outside dog beds when it’s going to rain… But we needed to know when it might rain. So I threw together this script in 30 minutes last night to poll weather.gov and send me an email when more than 30% probability of rain was coming up. Granted it’s dirty but I thought I’d share it since it’s a good 60 minute intro to doing this kind of thing, might help someone in the future trying to understand the how DWML works…

https://gist.github.com/apokalyptik/5946350

OSX Install qcachegrind (kcachedrind) from source the quick way

This article by Georgiana is pretty much awesomesauce. Now I can look at my cachegrind files without booting up a VM.  If you’re having trouble, here’s some help.  You probably want this file from the qt-project website.  It will install to ~/Qt/.  Then you’ll want to run

~/Qt/5.1.0/clang_64/bin/qmake -spec 'macx-g++'; make

The above will be helpful if you’re getting an error like this:

/Developer/Tools/Qt/uic generalsettings.ui -o ui_generalsettings.h
make: /Developer/Tools/Qt/uic: No such file or directory
make: *** [ui_generalsettings.h] Error 1

or a huge stream of errors like this:

../libviews/globalguiconfig.cpp: At global scope:
../libviews/globalguiconfig.cpp:188: error: no ‘QColor GlobalGUIConfig::groupColor(CostItem*)’ member function declared in class ‘GlobalGUIConfig’
../libviews/globalguiconfig.cpp: In member function ‘QColor GlobalGUIConfig::groupColor(CostItem*)’:
../libviews/globalguiconfig.cpp:188: error: return type ‘struct QColor’ is incomplete
../libviews/globalguiconfig.cpp:190: error: ‘class ConfigColorSetting’ has no member named ‘color’
../libviews/globalguiconfig.cpp: At global scope:
../libviews/globalguiconfig.cpp:193: error: no ‘QColor GlobalGUIConfig::eventTypeColor(EventType*)’ member function declared in class ‘GlobalGUIConfig’
../libviews/globalguiconfig.cpp: In member function ‘QColor GlobalGUIConfig::eventTypeColor(EventType*)’:
../libviews/globalguiconfig.cpp:193: error: return type ‘struct QColor’ is incomplete
../libviews/globalguiconfig.cpp:202: error: ‘class ConfigColorSetting’ has no member named ‘color’
../libviews/globalguiconfig.cpp: At global scope:
../libviews/globalguiconfig.cpp:206: error: no ‘QColor GlobalGUIConfig::functionColor(ProfileContext::Type, TraceFunction*)’ member function declared in class ‘GlobalGUIConfig’
../libviews/globalguiconfig.cpp: In member function ‘QColor GlobalGUIConfig::functionColor(ProfileContext::Type, TraceFunction*)’:
../libviews/globalguiconfig.cpp:206: error: return type ‘struct QColor’ is incomplete
../libviews/globalguiconfig.cpp:226: error: ‘class ConfigColorSetting’ has no member named ‘color’
make: *** [globalguiconfig.o] Error 1

 

Go Docs

Wish you had an offline version of http://golang.org/ (for reference when hacking on the bus, plane, train, etc)? is opening Check out the -http argument to the godoc binary (bundled with go)…. run godoc -http=:8888 And then open http://127.0.0.1:8888/ in your web browser. Boom. Since Go is named, well, “Go” it’s a real PITA to search for things on the internet. So searching for things like “offline go docs” and “offline golang docs” or “download go docs” and the like tend not to yeild useful results.

Hope this helps someone. I finally stumbled on this on my own after thinking “Fine. I’ll just generate my own!”