Learning Go (and reflect-ing on what’s possible, too)
It’s been a few days since my last Go post; my apologies. I know everyone out there was feverishly clicking refresh. Relax my babies, there’s more content on the way. This time, we’re going to look into the reflect package, which provides the introspection support found in Go.
If you’ll recall, before my last post, my intention was to cover the Go RPC functionality. I ran into a problem in that the gob package was used internally and I did not yet understand it. Well, as it turns out, the reflect package is also used. Values are inspected and translated into corresponding gob data. So this time, we’ll look at reflection within Go. This is an interesting topic as it not only prepares us for an upcoming RPC post, but it also really solidifies understanding of the Go type system.
Also, realize that I’m not an expert on Go. If you happen to be one with your Go-Fujitsu and you spot something incorrect (or even perhaps something not idiomatic), please leave a comment. That will help make me an expert!
The Inspected Code
In this example, we’ll dial back down to one source module, it’s just easier that way. We’ll be using the little gogogo command that we created in the first Go post. The first thing we’ll do is create a small collection of types and methods such that we can see what each component looks like when introspected. Here’s the source that we’ll actually be exploring at run time.
package main
import "reflect"
import "log"
type MailMessage struct {
subject string
from string
to string
body string
high_priority bool
}
/* Create a new mail message. */
func NewMailMessage(subject string, from string, to string) (*MailMessage) {
return &MailMessage{subject, from, to, "", false}
}
/* Set the message body. */
func (m *MailMessage)SetBody(body string) {
m.body = body
}
/* Set message priority. */
func (m *MailMessage)SetPriority(high bool) {
m.high_priority = high
}
/* Send the message */
func (m MailMessage) SendMessage() (bool) {
log.Printf("Message to %s has been sent\n", m.to)
return true
}
/* Typedef 'int' to a new MessagesSent type. */
type MessagesSent int
Note that at the top of this listing we also declare our package name and include the necessary import statements. If you’re following along and wish to compile, then you’ll wish comment out the line importing reflect as we’re not using it yet. As an aside, I’m not sure I like that feature of Go just yet.
A few notes on this example here.
- We define a new type, which is based on the structure. The new type contains a series of elements.
- We define three methods and one function. Two of the methods take a pointer to a MailMessage structure, whereas the third simply takes a MailMessage structure itself.
- We’re not doing anything. Nothing. This is a highly contrived example meant to highlight reflection. We’ll tackle the SMTP package down the road a little bit, you know, once we finally get that RPC stuff out of the way.
Great. Now that we have enough code that does nothing whatsoever, we’ll write some code that does something (with nothing). First, a bit of a detour.
The Type Switch
First, we’ll look at what Go deems the “Type Switch.” This gives us a mechanism, via a special syntax switch statement, to chose a code branch based on the underlying type of the object. Consider the following listing.
package main
import "log"
func main() {
x := 1
switch x_type := x.(type) {
case int:
log.Println("It Was An Integer")
}
}
This looks simple enough. Given x, we only want to print something if it is indeed an integer type. That special dot-type syntax you see above is what qualifies this as a type switch. However, take a look at what happens when we try to compile this code.
$ 6g type_switch.go type_switch.go:8: cannot type switch on non-interface value x (type int) $
That’s slightly confusing. It turns out that, in this case, x is being used as a object with a concrete type of integer. That is, it’s not being referenced via an interface. If we update the code, we can get the desired result.
package main
import "log"
func TypeSwitcher(v interface{}) {
switch v_type := v.(type) {
case int:
log.Println("It Was An Integer")
}
}
func main() {
TypeSwitcher(1)
}
Now, before we run this, note the interface{} type. That’s an empty interface. Empty interfaces can stand in for everything. Let’s build this again and give it a run, it should now print out what we’re expecting.
$ gogogo type_switch.go 2011/02/23 22:54:02 It Was An Integer
So, now you understand what a type switch is and how decisions based on type can be made in Go. However, what if we’re not checking against an interface? Or, rather, what if we want to know a little bit more about an object? Perhaps, say, how many fields a structure has? That’s where reflection comes in.
The Reflecting Code
So, like we’ve done in a few other examples, we’ll just dump the entire listing here and then walk through it afterwards. There are quite a few new elements here. Hang in there, we’ll explain them all.
/* Pull info we care about from reflect.Type */
func GetTypeInfo(t reflect.Type) (string, reflect.Kind, int) {
return t.Name(), t.Kind(), t.NumMethod()
}
/* Print information about a reflect.Value. */
func ReflectOnValue(v reflect.Value) {
/* Nothing to do on a nil. */
if v == nil {
log.Println("nil value, nothing to do")
return
}
/* Pull the information we require about the Type. */
name, kind, methods := GetTypeInfo(v.Type())
/* Now type switch and extract the Value Info. */
switch v := v.(type) {
case *reflect.StructValue:
log.Printf("Discovered %s/%s that has %d fields and %d methods.\n",
kind, name, v.NumField(), methods)
case *reflect.PtrValue:
log.Printf("Discovered a pointer with %d methods, dereferencing...\n", methods)
ReflectOnValue(v.Elem())
default:
log.Printf("Found %s/%s with %d methods.\n", kind, name, methods)
}
}
/* Make anything an interface. */
func PrintValueData(i interface{}) {
ReflectOnValue(reflect.NewValue(i))
}
Alright! This code is responsible for digging into the details of our objects and reporting what it finds. Let’s walk through it (almost) line by line and examine exactly what’s going on.
- The GetTypeInfo function takes a single argument, a reflect.Type, and returns a string, a reflect.Kind, and an integer value. This little function returns the name of the type in question, the underlying kind, and the number of methods associated with this type in receiver context.
- The ReflectOnValue function takes a reflect.Value object v and returns nothing.
- First, if v is nil, we simple print that out and return. There’s no reason to go any further.
- Next, we call GetTypeInfo and pull information about this value’s corresponding type. That’s important. In short, this means that each value has a type that contains information such as which methods are associated with it.
- Next up, another type switch. This time, however, we’re switching on the reflect value and not the original value. This allows us to branch according to what the value is.
- The first option is a *reflect.StructValue. So, if v’s type is pointer-to-struct-value, then we’ll print out the name of the struct, the fact that it is a struct, the number of fields associated with it, and the number of methods it has. There are calls which allow us to individually access each field by name and number, and to access individual methods as well. We skim over that in this example, however.
- The next option is very interesting – *reflect.PtrValue. Or, in English, a pointer to a pointer value. This is not the same as a pointer to a pointer! Since pointer types can have methods associated with them, we print the number here. Next, we dereference the pointer via “pointer.Elem()” and recursively call ourself. This implies that pointers aren’t true pointers, rather, a flavor of smart pointer.
- The last thing we do in this function is print any “standard” types, followed by the number of methods they have associated with them.
- Finally, we have PrintValueData. This little helper function simply translates any given value into a reflect.Value, to allow for processing.
The Driver
Our goal here is to see how different types and values are viewed by the Go system. We create a series of variables in our main method and send them to our PrintValueData function.
func main() {
/* Mail Message Objects */
m_ptr := NewMailMessage("subject", "from", "to")
m_std := MailMessage{"subject", "from", "to", "body", false}
var counter MessagesSent
/* Print out Information about each. */
PrintValueData(0)
PrintValueData(nil)
PrintValueData(NewMailMessage)
PrintValueData(m_std)
PrintValueData(&m_ptr)
PrintValueData(counter)
}
Running the Application
Now we have a complete application. Let’s see what happens when we run it.
$ gogogo reflect.go 2011/02/24 00:30:45 Found int/int with 0 methods. 2011/02/24 00:30:45 nil value, nothing to do 2011/02/24 00:30:45 Found func/ with 0 methods. 2011/02/24 00:30:45 Discovered struct/MailMessage that has 5 fields and 1 methods. 2011/02/24 00:30:45 Discovered a pointer with 0 methods, dereferencing... 2011/02/24 00:30:45 Discovered a pointer with 3 methods, dereferencing... 2011/02/24 00:30:45 Discovered struct/MailMessage that has 5 fields and 1 methods. 2011/02/24 00:30:45 Found int/MessagesSent with 0 methods. $
Wow, neat. This output is almost what we expected. Let’s again go through this line-by-line and look at why we’ve got what we have. Don’t worry if method information seems a little odd to you, we’ll double back to that afterwards.
- Handled by our default case. This is simply an integer with zero methods.
- Handled by our nil test.
- Handled by our default handler. Go identifies it as a function (with no type name) that has zero of its own methods.
- Handled by our structure case. We pass in m_std in our main function, which is a pass-by-value function. Go tells us that this structure has five fields and one method associated with it.
- Go finds a pointer with zero methods attached to it, and dereferences.
- Go finds a pointer with one three methods attached to it, and dereferences.
- Go uncovers what we pointed to by &m_ptr (the address of a pointer to a structure of kind MailMessage). It correctly prints the same information as noted on line four.
- Go prints that we have a type MessagesSent with a underlying kind of int, with zero attached methods.
Ok, what’s wrong with this picture? Anyone paying attention? We defined two methods that take a pointer to a MailMessage and one method that takes a MailMessage by value. However, when we dereferenced down to a MailMessage structure, we only accounted for one method. At the same time, we have a pointer (m_ptr after dereferencing &m_ptr), that has three methods! Why?
So, in Go, a pointer to a type’s set of methods is the union of all methods both on the pointer type as well as the pointed-to type. So, *MailMessage has methods SetBody, SetPriority, and SendMessage associated with it. Of those methods, only SendMessages takes a MailMessage by value, thus it only has one method associated with it.
Final Thoughts
So, at this point, we’ve covered a bit about reflection. The Go documentation on this is slightly sparse. I pieced this entry together using both the Go reflect documentation, and the source code to the Go json module. So, if I’m off base on anything, please leave a comment and correct me.
Next, it is possible to dynamically dispatch methods using the Call method of a reflect.FuncValue object. This method takes a slice of reflect.Value and returns a slice of reflect.Value. Rigging this up would be an interesting experiment.
Lastly, it is not possible in Go to create new types at runtime.


on February 24, 2011 at 12:52 pm
Permalink
[...] This post was mentioned on Twitter by Julio Berdote, Jeff McNeil. Jeff McNeil said: http://ow.ly/42oAR Another #Go post, this time on reflection. Had to piece this together from reflect docs & json module docs. #golang [...]
on February 27, 2011 at 5:53 pm
Permalink
Hi, tripped over this post in my search for some background on introspection in Go, and wanted to just simplify (or pritty-fy) your code a bit. I don’t know if you know, or if you are simply doing it for pedagogical reasons, but you can leave out the parenthesis around the return in functions and methods if it only returns a single element, the parenthesis is only for multi-variable returns (but as you undoubtably have discovered, it doesn’t break anything but my mild OCD
/* Create a new mail message. */
func NewMailMessage(subject string, from string, to string) *MailMessage {
return &MailMessage{subject, from, to, “”, false}
}
/* Send the message */
func (m MailMessage) SendMessage() bool {
log.Printf(“Message to %s has been sent\n”, m.to)
return true
}
It simply makes everything a bit easier on the eyes. Enjoying your examples, keep it up.