From 6652b0b86639a6a59e038bcb85b18fd1c1f25a95 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Thu, 1 Mar 2012 10:05:51 +1100 Subject: [PATCH] doc: add The Laws of Reflection article Originally published on The Go Programming Language Blog, September 6, 2011. http://blog.golang.org/2011/09/laws-of-reflection.html Update #2547 R=golang-dev, r, adg CC=golang-dev https://golang.org/cl/5689054 --- doc/Makefile | 1 + doc/articles/laws_of_reflection.html | 752 +++++++++++++++++++++++++++ doc/articles/laws_of_reflection.tmpl | 654 +++++++++++++++++++++++ doc/docs.html | 3 +- doc/progs/interface.go | 56 ++ doc/progs/interface2.go | 112 ++++ src/pkg/reflect/type.go | 2 +- 7 files changed, 1577 insertions(+), 3 deletions(-) create mode 100644 doc/articles/laws_of_reflection.html create mode 100644 doc/articles/laws_of_reflection.tmpl create mode 100644 doc/progs/interface.go create mode 100644 doc/progs/interface2.go diff --git a/doc/Makefile b/doc/Makefile index 40e8569b23e..ff69bc775c0 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -6,6 +6,7 @@ HTML=\ articles/defer_panic_recover.html\ articles/error_handling.html\ articles/slices_usage_and_internals.html\ + articles/laws_of_reflection.html\ effective_go.html\ go1.html\ diff --git a/doc/articles/laws_of_reflection.html b/doc/articles/laws_of_reflection.html new file mode 100644 index 00000000000..4df70e0d2cb --- /dev/null +++ b/doc/articles/laws_of_reflection.html @@ -0,0 +1,752 @@ + + + + +

+Reflection in computing is the +ability of a program to examine its own structure, particularly +through types; it's a form of metaprogramming. It's also a great +source of confusion. +

+ +

+In this article we attempt to clarify things by explaining how +reflection works in Go. Each language's reflection model is +different (and many languages don't support it at all), but +this article is about Go, so for the rest of this article the word +"reflection" should be taken to mean "reflection in Go". +

+ +

Types and interfaces

+ +

+Because reflection builds on the type system, let's start with a +refresher about types in Go. +

+ +

+Go is statically typed. Every variable has a static type, that is, +exactly one type known and fixed at compile time: int, +float32, *MyType, []byte, +and so on. If we declare +

+ +
type MyInt int
+
+var i int
+var j MyInt
+ +

+then i has type int and j +has type MyInt. The variables i and +j have distinct static types and, although they have +the same underlying type, they cannot be assigned to one another +without a conversion. +

+ +

+One important category of type is interface types, which represent +fixed sets of methods. An interface variable can store any concrete +(non-interface) value as long as that value implements the +interface's methods. A well-known pair of examples is +io.Reader and io.Writer, the types +Reader and Writer from the io package: +

+ +
// Reader is the interface that wraps the basic Read method.
+type Reader interface {
+    Read(p []byte) (n int, err error)
+}
+
+// Writer is the interface that wraps the basic Write method.
+type Writer interface {
+    Write(p []byte) (n int, err error)
+}
+ +

+Any type that implements a Read (or +Write) method with this signature is said to implement +io.Reader (or io.Writer). For the +purposes of this discussion, that means that a variable of type +io.Reader can hold any value whose type has a +Read method: +

+ +
    var r io.Reader
+    r = os.Stdin
+    r = bufio.NewReader(r)
+    r = new(bytes.Buffer)
+    // and so on
+ +

+It's important to be clear that whatever concrete value +r may hold, r's type is always +io.Reader: Go is statically typed and the static type +of r is io.Reader.

+ +

+An extremely important example of an interface type is the empty +interface: +

+ +
+interface{}
+
+ +

+It represents the empty set of methods and is satisfied by any +value at all, since any value has zero or more methods. +

+ +

+Some people say that Go's interfaces are dynamically typed, but +that is misleading. They are statically typed: a variable of +interface type always has the same static type, and even though at +run time the value stored in the interface variable may change +type, that value will always satisfy the interface. +

+ +

+We need to be precise about all this because reflection and +interfaces are closely related. +

+ +

The representation of an interface

+ +

+Russ Cox has written a +detailed blog post about the representation of interface values +in Go. It's not necessary to repeat the full story here, but a +simplified summary is in order. +

+ +

+A variable of interface type stores a pair: the concrete value +assigned to the variable, and that value's type descriptor. +To be more precise, the value is the underlying concrete data item +that implements the interface and the type describes the full type +of that item. For instance, after +

+ +
    var r io.Reader
+    tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
+    if err != nil {
+        return nil, err
+    }
+    r = tty
+ +

+r contains, schematically, the (value, type) pair, +(tty, *os.File). Notice that the type +*os.File implements methods other than +Read; even though the interface value provides access +only to the Read method, the value inside carries all +the type information about that value. That's why we can do things +like this: +

+ +
    var w io.Writer
+    w = r.(io.Writer)
+ +

+The expression in this assignment is a type assertion; what it +asserts is that the item inside r also implements +io.Writer, and so we can assign it to w. +After the assignment, w will contain the pair +(tty, *os.File). That's the same pair as +was held in r. The static type of the interface +determines what methods may be invoked with an interface variable, +even though the concrete value inside may have a larger set of +methods. +

+ +

+Continuing, we can do this: +

+ +
    var empty interface{}
+    empty = w
+ +

+and our empty interface value e will again contain +that same pair, (tty, *os.File). That's +handy: an empty interface can hold any value and contains all the +information we could ever need about that value. +

+ +

+(We don't need a type assertion here because it's known statically +that w satisfies the empty interface. In the example +where we moved a value from a Reader to a +Writer, we needed to be explicit and use a type +assertion because Writer's methods are not a +subset of Reader's.) +

+ +

+One important detail is that the pair inside an interface always +has the form (value, concrete type) and cannot have the form +(value, interface type). Interfaces do not hold interface +values. +

+ +

+Now we're ready to reflect. +

+ +

The first law of reflection

+ +

1. Reflection goes from interface value to reflection object.

+ +

+At the basic level, reflection is just a mechanism to examine the +type and value pair stored inside an interface variable. To get +started, there are two types we need to know about in +package reflect: +Typeand +Value. Those two types +give access to the contents of an interface variable, and two +simple functions, called reflect.TypeOf and +reflect.ValueOf, retrieve reflect.Type +and reflect.Value pieces out of an interface value. +(Also, from the reflect.Value it's easy to get +to the reflect.Type, but let's keep the +Value and Type concepts separate for +now.) +

+ +

+Let's start with TypeOf: +

+ +
package main
+
+import (
+    "fmt"
+    "reflect"
+)
+
+func main() {
+    var x float64 = 3.4
+    fmt.Println("type:", reflect.TypeOf(x))
+}
+ +

+This program prints +

+ +
+type: float64
+
+ +

+You might be wondering where the interface is here, since the +program looks like it's passing the float64 +variable x, not an interface value, to +reflect.TypeOf. But it's there; as godoc reports, the +signature of reflect.TypeOf includes an empty +interface: +

+ +
+// TypeOf returns the reflection Type of the value in the interface{}.
+func TypeOf(i interface{}) Type
+
+ +

+When we call reflect.TypeOf(x), x is +first stored in an empty interface, which is then passed as the +argument; reflect.TypeOf unpacks that empty interface +to recover the type information. +

+ +

+The reflect.ValueOf function, of course, recovers the +value (from here on we'll elide the boilerplate and focus just on +the executable code): +

+ +
    var x float64 = 3.4
+    fmt.Println("type:", reflect.TypeOf(x))
+ +

+prints +

+ +
+value: <float64 Value>
+
+ +

+Both reflect.Type and reflect.Value have +lots of methods to let us examine and manipulate them. One +important example is that Value has a +Type method that returns the Type of a +reflect.Value. Another is that both Type +and Value have a Kind method that returns +a constant indicating what sort of item is stored: +Uint, Float64, Slice, and so +on. Also methods on Value with names like +Int and Float let us grab values (as +int64 and float64) stored inside: +

+ +
    var x float64 = 3.4
+    v := reflect.ValueOf(x)
+    fmt.Println("type:", v.Type())
+    fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
+    fmt.Println("value:", v.Float())
+ +

+prints +

+ +
+type: float64
+kind is float64: true
+value: 3.4
+
+ +

+There are also methods like SetInt and +SetFloat but to use them we need to understand +settability, the subject of the third law of reflection, discussed +below. +

+ +

+The reflection library has a couple of properties worth singling +out. First, to keep the API simple, the "getter" and "setter" +methods of Value operate on the largest type that can +hold the value: int64 for all the signed integers, for +instance. That is, the Int method of +Value returns an int64 and the +SetInt value takes an int64; it may be +necessary to convert to the actual type involved: +

+ +
    var x uint8 = 'x'
+    v := reflect.ValueOf(x)
+    fmt.Println("type:", v.Type())                            // uint8.
+    fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.
+    x = uint8(v.Uint())                                       // v.Uint returns a uint64.
+ +

+The second property is that the Kind of a reflection +object describes the underlying type, not the static type. If a +reflection object contains a value of a user-defined integer type, +as in +

+ +
    type MyInt int
+    var x MyInt = 7
+    v := reflect.ValueOf(x)
+ +

+the Kind of v is still +reflect.Int, even though the static type of +x is MyInt, not int. In +other words, the Kind cannot discriminate an int from +a MyInt even though the Type can. +

+ +

The second law of reflection

+ +

2. Reflection goes from reflection object to interface +value.

+ +

+Like physical reflection, reflection in Go generates its own +inverse. +

+ +

+Given a reflect.Value we can recover an interface +value using the Interface method; in effect the method +packs the type and value information back into an interface +representation and returns the result: +

+ +
+// Interface returns v's value as an interface{}.
+func (v Value) Interface() interface{}
+
+ +

+As a consequence we can say +

+ +
    y := v.Interface().(float64) // y will have type float64.
+    fmt.Println(y)
+ +

+to print the float64 value represented by the +reflection object v. +

+ +

+We can do even better, though. The arguments to +fmt.Println, fmt.Printf and so on are all +passed as empty interface values, which are then unpacked by the +fmt package internally just as we have been doing in +the previous examples. Therefore all it takes to print the contents +of a reflect.Value correctly is to pass the result of +the Interface method to the formatted print +routine: +

+ +
    fmt.Println(v.Interface())
+ +

+(Why not fmt.Println(v)? Because v is a +reflect.Value; we want the concrete value it holds.) +Since our value is a float64, we can even use a +floating-point format if we want: +

+ +
    fmt.Printf("value is %7.1e\n", v.Interface())
+ +

+and get in this case +

+ +
+3.4e+00
+
+ +

+Again, there's no need to type-assert the result of +v.Interface() to float64; the empty +interface value has the concrete value's type information inside +and Printf will recover it. +

+ +

+In short, the Interface method is the inverse of the +ValueOf function, except that its result is always of +static type interface{}. +

+ +

+Reiterating: Reflection goes from interface values to reflection +objects and back again. +

+ +

The third law of reflection

+ +

3. To modify a reflection object, the value must be settable.

+ +

+The third law is the most subtle and confusing, but it's easy +enough to understand if we start from first principles. +

+ +

+Here is some code that does not work, but is worth studying. +

+ +
    var x float64 = 3.4
+    v := reflect.ValueOf(x)
+    v.SetFloat(7.1) // Error: will panic.
+ +

+If you run this code, it will panic with the cryptic message +

+ +
+panic: reflect.Value.SetFloat using unaddressable value
+
+ +

+The problem is not that the value 7.1 is not +addressable; it's that v is not settable. Settability +is a property of a reflection Value, and not all +reflection Values have it. +

+ +

+The CanSet method of Value reports the +settability of a Value; in our case, +

+ +
    var x float64 = 3.4
+    v := reflect.ValueOf(x)
+    fmt.Println("settability of v:", v.CanSet())
+ +

+prints +

+ +
+settability of v: false
+
+ +

+It is an error to call a Set method on an non-settable +Value. But what is settability? +

+ +

+Settability is a bit like addressability, but stricter. It's the +property that a reflection object can modify the actual storage +that was used to create the reflection object. Settability is +determined by whether the reflection object holds the original +item. When we say +

+ +
    var x float64 = 3.4
+    v := reflect.ValueOf(x)
+ +

+we pass a copy of x to +reflect.ValueOf, so the interface value created as the +argument to reflect.ValueOf is a copy of +x, not x itself. Thus, if the +statement +

+ +
    v.SetFloat(7.1)
+ +

+were allowed to succeed, it would not update x, even +though v looks like it was created from +x. Instead, it would update the copy of x +stored inside the reflection value and x itself would +be unaffected. That would be confusing and useless, so it is +illegal, and settability is the property used to avoid this +issue. +

+ +

+If this seems bizarre, it's not. It's actually a familiar situation +in unusual garb. Think of passing x to a +function: +

+ +
+f(x)
+
+ +

+We would not expect f to be able to modify +x because we passed a copy of x's value, +not x itself. If we want f to modify +x directly we must pass our function the address of +x (that is, a pointer to x):

+ +

+f(&x) +

+ +

+This is straightforward and familiar, and reflection works the same +way. If we want to modify x by reflection, we must +give the reflection library a pointer to the value we want to +modify. +

+ +

+Let's do that. First we initialize x as usual +and then create a reflection value that points to it, called +p. +

+ +
    var x float64 = 3.4
+    p := reflect.ValueOf(&x) // Note: take the address of x.
+    fmt.Println("type of p:", p.Type())
+    fmt.Println("settability of p:", p.CanSet())
+ +

+The output so far is +

+ +
+type of p: *float64
+settability of p: false
+
+ +

+The reflection object p isn't settable, but it's not +p we want to set, it's (in effect) *p. To +get to what p points to, we call the Elem +method of Value, which indirects through the pointer, +and save the result in a reflection Value called +v: +

+ +
    v := p.Elem()
+    fmt.Println("settability of v:", v.CanSet())
+ +

+Now v is a settable reflection object, as the output +demonstrates, +

+ +
+settability of v: true
+
+ +

+and since it represents x, we are finally able to use +v.SetFloat to modify the value of +x: +

+ +
    v.SetFloat(7.1)
+    fmt.Println(v.Interface())
+    fmt.Println(x)
+ +

+The output, as expected, is +

+ +
+7.1
+7.1
+
+ +

+Reflection can be hard to understand but it's doing exactly what +the language does, albeit through reflection Types and +Values that can disguise what's going on. Just keep in +mind that reflection Values need the address of something in order +to modify what they represent. +

+ +

Structs

+ +

+In our previous example v wasn't a pointer itself, it +was just derived from one. A common way for this situation to arise +is when using reflection to modify the fields of a structure. As +long as we have the address of the structure, we can modify its +fields. +

+ +

+Here's a simple example that analyzes a struct value, +t. We create the reflection object with the address of +the struct because we'll want to modify it later. Then we set +typeOfT to its type and iterate over the fields using +straightforward method calls (see +package reflect for details). +Note that we extract the names of the fields from the struct type, +but the fields themselves are regular reflect.Value +objects. +

+ +
    type T struct {
+        A int
+        B string
+    }
+    t := T{23, "skidoo"}
+    s := reflect.ValueOf(&t).Elem()
+    typeOfT := s.Type()
+    for i := 0; i < s.NumField(); i++ {
+        f := s.Field(i)
+        fmt.Printf("%d: %s %s = %v\n", i,
+            typeOfT.Field(i).Name, f.Type(), f.Interface())
+    }
+    s.Field(0).SetInt(77)
+    s.Field(1).SetString("Sunset Strip")
+    fmt.Println("t is now", t)
+ +

+The output of this program is +

+ +
+0: A int = 23
+1: B string = skidoo
+
+ +

+There's one more point about settability introduced in +passing here: the field names of T are upper case +(exported) because only exported fields of a struct are +settable. +

+ +

+Because s contains a settable reflection object, we +can modify the fields of the structure. +

+ +
    s.Field(0).SetInt(77)
+    s.Field(1).SetString("Sunset Strip")
+    fmt.Println("t is now", t)
+ +

+And here's the result: +

+ +
+t is now {77 Sunset Strip}
+
+ +

+If we modified the program so that s was created from +t, not &t, the calls to +SetInt and SetString would fail as the +fields of t would not be settable. +

+ +

Conclusion

+ +

+Here again are the laws of reflection: +

+ +
    +
  1. Reflection goes from interface value to reflection +object.
  2. +
  3. Reflection goes from reflection object to interface +value.
  4. +
  5. To modify a reflection object, the value must be settable.
  6. +
+ +

+Once you understand these laws reflection in Go becomes much easier +to use, although it remains subtle. It's a powerful tool that +should be used with care and avoided unless strictly +necessary. +

+ +

+There's plenty more to reflection that we haven't covered — +sending and receiving on channels, allocating memory, using slices +and maps, calling methods and functions — but this post is +long enough. We'll cover some of those topics in a later +article. +

\ No newline at end of file diff --git a/doc/articles/laws_of_reflection.tmpl b/doc/articles/laws_of_reflection.tmpl new file mode 100644 index 00000000000..7db5d6d3b56 --- /dev/null +++ b/doc/articles/laws_of_reflection.tmpl @@ -0,0 +1,654 @@ + +{{donotedit}} + +

+Reflection in computing is the +ability of a program to examine its own structure, particularly +through types; it's a form of metaprogramming. It's also a great +source of confusion. +

+ +

+In this article we attempt to clarify things by explaining how +reflection works in Go. Each language's reflection model is +different (and many languages don't support it at all), but +this article is about Go, so for the rest of this article the word +"reflection" should be taken to mean "reflection in Go". +

+ +

Types and interfaces

+ +

+Because reflection builds on the type system, let's start with a +refresher about types in Go. +

+ +

+Go is statically typed. Every variable has a static type, that is, +exactly one type known and fixed at compile time: int, +float32, *MyType, []byte, +and so on. If we declare +

+ +{{code "progs/interface.go" `/type MyInt/` `/STOP/`}} + +

+then i has type int and j +has type MyInt. The variables i and +j have distinct static types and, although they have +the same underlying type, they cannot be assigned to one another +without a conversion. +

+ +

+One important category of type is interface types, which represent +fixed sets of methods. An interface variable can store any concrete +(non-interface) value as long as that value implements the +interface's methods. A well-known pair of examples is +io.Reader and io.Writer, the types +Reader and Writer from the io package: +

+ +{{code "progs/interface.go" `/// Reader/` `/STOP/`}} + +

+Any type that implements a Read (or +Write) method with this signature is said to implement +io.Reader (or io.Writer). For the +purposes of this discussion, that means that a variable of type +io.Reader can hold any value whose type has a +Read method: +

+ +{{code "progs/interface.go" `/func readers/` `/STOP/`}} + +

+It's important to be clear that whatever concrete value +r may hold, r's type is always +io.Reader: Go is statically typed and the static type +of r is io.Reader.

+ +

+An extremely important example of an interface type is the empty +interface: +

+ +
+interface{}
+
+ +

+It represents the empty set of methods and is satisfied by any +value at all, since any value has zero or more methods. +

+ +

+Some people say that Go's interfaces are dynamically typed, but +that is misleading. They are statically typed: a variable of +interface type always has the same static type, and even though at +run time the value stored in the interface variable may change +type, that value will always satisfy the interface. +

+ +

+We need to be precise about all this because reflection and +interfaces are closely related. +

+ +

The representation of an interface

+ +

+Russ Cox has written a +detailed blog post about the representation of interface values +in Go. It's not necessary to repeat the full story here, but a +simplified summary is in order. +

+ +

+A variable of interface type stores a pair: the concrete value +assigned to the variable, and that value's type descriptor. +To be more precise, the value is the underlying concrete data item +that implements the interface and the type describes the full type +of that item. For instance, after +

+ +{{code "progs/interface.go" `/func typeAssertions/` `/STOP/`}} + +

+r contains, schematically, the (value, type) pair, +(tty, *os.File). Notice that the type +*os.File implements methods other than +Read; even though the interface value provides access +only to the Read method, the value inside carries all +the type information about that value. That's why we can do things +like this: +

+ +{{code "progs/interface.go" `/var w io.Writer/` `/STOP/`}} + +

+The expression in this assignment is a type assertion; what it +asserts is that the item inside r also implements +io.Writer, and so we can assign it to w. +After the assignment, w will contain the pair +(tty, *os.File). That's the same pair as +was held in r. The static type of the interface +determines what methods may be invoked with an interface variable, +even though the concrete value inside may have a larger set of +methods. +

+ +

+Continuing, we can do this: +

+ +{{code "progs/interface.go" `/var empty interface{}/` `/STOP/`}} + +

+and our empty interface value e will again contain +that same pair, (tty, *os.File). That's +handy: an empty interface can hold any value and contains all the +information we could ever need about that value. +

+ +

+(We don't need a type assertion here because it's known statically +that w satisfies the empty interface. In the example +where we moved a value from a Reader to a +Writer, we needed to be explicit and use a type +assertion because Writer's methods are not a +subset of Reader's.) +

+ +

+One important detail is that the pair inside an interface always +has the form (value, concrete type) and cannot have the form +(value, interface type). Interfaces do not hold interface +values. +

+ +

+Now we're ready to reflect. +

+ +

The first law of reflection

+ +

1. Reflection goes from interface value to reflection object.

+ +

+At the basic level, reflection is just a mechanism to examine the +type and value pair stored inside an interface variable. To get +started, there are two types we need to know about in +package reflect: +Typeand +Value. Those two types +give access to the contents of an interface variable, and two +simple functions, called reflect.TypeOf and +reflect.ValueOf, retrieve reflect.Type +and reflect.Value pieces out of an interface value. +(Also, from the reflect.Value it's easy to get +to the reflect.Type, but let's keep the +Value and Type concepts separate for +now.) +

+ +

+Let's start with TypeOf: +

+ +{{code "progs/interface2.go" `/package main/` `/STOP main/`}} + +

+This program prints +

+ +
+type: float64
+
+ +

+You might be wondering where the interface is here, since the +program looks like it's passing the float64 +variable x, not an interface value, to +reflect.TypeOf. But it's there; as godoc reports, the +signature of reflect.TypeOf includes an empty +interface: +

+ +
+// TypeOf returns the reflection Type of the value in the interface{}.
+func TypeOf(i interface{}) Type
+
+ +

+When we call reflect.TypeOf(x), x is +first stored in an empty interface, which is then passed as the +argument; reflect.TypeOf unpacks that empty interface +to recover the type information. +

+ +

+The reflect.ValueOf function, of course, recovers the +value (from here on we'll elide the boilerplate and focus just on +the executable code): +

+ +{{code "progs/interface2.go" `/var x/` `/STOP/`}} + +

+prints +

+ +
+value: <float64 Value>
+
+ +

+Both reflect.Type and reflect.Value have +lots of methods to let us examine and manipulate them. One +important example is that Value has a +Type method that returns the Type of a +reflect.Value. Another is that both Type +and Value have a Kind method that returns +a constant indicating what sort of item is stored: +Uint, Float64, Slice, and so +on. Also methods on Value with names like +Int and Float let us grab values (as +int64 and float64) stored inside: +

+ +{{code "progs/interface2.go" `/START f1/` `/STOP/`}} + +

+prints +

+ +
+type: float64
+kind is float64: true
+value: 3.4
+
+ +

+There are also methods like SetInt and +SetFloat but to use them we need to understand +settability, the subject of the third law of reflection, discussed +below. +

+ +

+The reflection library has a couple of properties worth singling +out. First, to keep the API simple, the "getter" and "setter" +methods of Value operate on the largest type that can +hold the value: int64 for all the signed integers, for +instance. That is, the Int method of +Value returns an int64 and the +SetInt value takes an int64; it may be +necessary to convert to the actual type involved: +

+ +{{code "progs/interface2.go" `/START f2/` `/STOP/`}} + +

+The second property is that the Kind of a reflection +object describes the underlying type, not the static type. If a +reflection object contains a value of a user-defined integer type, +as in +

+ +{{code "progs/interface2.go" `/START f3/` `/START/`}} + +

+the Kind of v is still +reflect.Int, even though the static type of +x is MyInt, not int. In +other words, the Kind cannot discriminate an int from +a MyInt even though the Type can. +

+ +

The second law of reflection

+ +

2. Reflection goes from reflection object to interface +value.

+ +

+Like physical reflection, reflection in Go generates its own +inverse. +

+ +

+Given a reflect.Value we can recover an interface +value using the Interface method; in effect the method +packs the type and value information back into an interface +representation and returns the result: +

+ +
+// Interface returns v's value as an interface{}.
+func (v Value) Interface() interface{}
+
+ +

+As a consequence we can say +

+ +{{code "progs/interface2.go" `/START f3b/` `/START/`}} + +

+to print the float64 value represented by the +reflection object v. +

+ +

+We can do even better, though. The arguments to +fmt.Println, fmt.Printf and so on are all +passed as empty interface values, which are then unpacked by the +fmt package internally just as we have been doing in +the previous examples. Therefore all it takes to print the contents +of a reflect.Value correctly is to pass the result of +the Interface method to the formatted print +routine: +

+ +{{code "progs/interface2.go" `/START f3c/` `/START/`}} + +

+(Why not fmt.Println(v)? Because v is a +reflect.Value; we want the concrete value it holds.) +Since our value is a float64, we can even use a +floating-point format if we want: +

+ +{{code "progs/interface2.go" `/START f3d/` `/STOP/`}} + +

+and get in this case +

+ +
+3.4e+00
+
+ +

+Again, there's no need to type-assert the result of +v.Interface() to float64; the empty +interface value has the concrete value's type information inside +and Printf will recover it. +

+ +

+In short, the Interface method is the inverse of the +ValueOf function, except that its result is always of +static type interface{}. +

+ +

+Reiterating: Reflection goes from interface values to reflection +objects and back again. +

+ +

The third law of reflection

+ +

3. To modify a reflection object, the value must be settable.

+ +

+The third law is the most subtle and confusing, but it's easy +enough to understand if we start from first principles. +

+ +

+Here is some code that does not work, but is worth studying. +

+ +{{code "progs/interface2.go" `/START f4/` `/STOP/`}} + +

+If you run this code, it will panic with the cryptic message +

+ +
+panic: reflect.Value.SetFloat using unaddressable value
+
+ +

+The problem is not that the value 7.1 is not +addressable; it's that v is not settable. Settability +is a property of a reflection Value, and not all +reflection Values have it. +

+ +

+The CanSet method of Value reports the +settability of a Value; in our case, +

+ +{{code "progs/interface2.go" `/START f5/` `/STOP/`}} + +

+prints +

+ +
+settability of v: false
+
+ +

+It is an error to call a Set method on an non-settable +Value. But what is settability? +

+ +

+Settability is a bit like addressability, but stricter. It's the +property that a reflection object can modify the actual storage +that was used to create the reflection object. Settability is +determined by whether the reflection object holds the original +item. When we say +

+ +{{code "progs/interface2.go" `/START f6/` `/START/`}} + +

+we pass a copy of x to +reflect.ValueOf, so the interface value created as the +argument to reflect.ValueOf is a copy of +x, not x itself. Thus, if the +statement +

+ +{{code "progs/interface2.go" `/START f6b/` `/STOP/`}} + +

+were allowed to succeed, it would not update x, even +though v looks like it was created from +x. Instead, it would update the copy of x +stored inside the reflection value and x itself would +be unaffected. That would be confusing and useless, so it is +illegal, and settability is the property used to avoid this +issue. +

+ +

+If this seems bizarre, it's not. It's actually a familiar situation +in unusual garb. Think of passing x to a +function: +

+ +
+f(x)
+
+ +

+We would not expect f to be able to modify +x because we passed a copy of x's value, +not x itself. If we want f to modify +x directly we must pass our function the address of +x (that is, a pointer to x):

+ +

+f(&x) +

+ +

+This is straightforward and familiar, and reflection works the same +way. If we want to modify x by reflection, we must +give the reflection library a pointer to the value we want to +modify. +

+ +

+Let's do that. First we initialize x as usual +and then create a reflection value that points to it, called +p. +

+ +{{code "progs/interface2.go" `/START f7/` `/START/`}} + +

+The output so far is +

+ +
+type of p: *float64
+settability of p: false
+
+ +

+The reflection object p isn't settable, but it's not +p we want to set, it's (in effect) *p. To +get to what p points to, we call the Elem +method of Value, which indirects through the pointer, +and save the result in a reflection Value called +v: +

+ +{{code "progs/interface2.go" `/START f7b/` `/START/`}} + +

+Now v is a settable reflection object, as the output +demonstrates, +

+ +
+settability of v: true
+
+ +

+and since it represents x, we are finally able to use +v.SetFloat to modify the value of +x: +

+ +{{code "progs/interface2.go" `/START f7c/` `/STOP/`}} + +

+The output, as expected, is +

+ +
+7.1
+7.1
+
+ +

+Reflection can be hard to understand but it's doing exactly what +the language does, albeit through reflection Types and +Values that can disguise what's going on. Just keep in +mind that reflection Values need the address of something in order +to modify what they represent. +

+ +

Structs

+ +

+In our previous example v wasn't a pointer itself, it +was just derived from one. A common way for this situation to arise +is when using reflection to modify the fields of a structure. As +long as we have the address of the structure, we can modify its +fields. +

+ +

+Here's a simple example that analyzes a struct value, +t. We create the reflection object with the address of +the struct because we'll want to modify it later. Then we set +typeOfT to its type and iterate over the fields using +straightforward method calls (see +package reflect for details). +Note that we extract the names of the fields from the struct type, +but the fields themselves are regular reflect.Value +objects. +

+ +{{code "progs/interface2.go" `/START f8/` `/STOP/`}} + +

+The output of this program is +

+ +
+0: A int = 23
+1: B string = skidoo
+
+ +

+There's one more point about settability introduced in +passing here: the field names of T are upper case +(exported) because only exported fields of a struct are +settable. +

+ +

+Because s contains a settable reflection object, we +can modify the fields of the structure. +

+ +{{code "progs/interface2.go" `/START f8b/` `/STOP/`}} + +

+And here's the result: +

+ +
+t is now {77 Sunset Strip}
+
+ +

+If we modified the program so that s was created from +t, not &t, the calls to +SetInt and SetString would fail as the +fields of t would not be settable. +

+ +

Conclusion

+ +

+Here again are the laws of reflection: +

+ +
    +
  1. Reflection goes from interface value to reflection +object.
  2. +
  3. Reflection goes from reflection object to interface +value.
  4. +
  5. To modify a reflection object, the value must be settable.
  6. +
+ +

+Once you understand these laws reflection in Go becomes much easier +to use, although it remains subtle. It's a powerful tool that +should be used with care and avoided unless strictly +necessary. +

+ +

+There's plenty more to reflection that we haven't covered — +sending and receiving on channels, allocating memory, using slices +and maps, calling methods and functions — but this post is +long enough. We'll cover some of those topics in a later +article. +

\ No newline at end of file diff --git a/doc/docs.html b/doc/docs.html index ccffad8a188..449e233ad19 100644 --- a/doc/docs.html +++ b/doc/docs.html @@ -91,7 +91,7 @@ Guided tours of Go programs. @@ -253,4 +253,3 @@ Go libraries.

  • A Tour of Go
  • golang-korea - Go documentation and news.
  • - diff --git a/doc/progs/interface.go b/doc/progs/interface.go new file mode 100644 index 00000000000..91145401e22 --- /dev/null +++ b/doc/progs/interface.go @@ -0,0 +1,56 @@ +package main + +import ( + "bufio" + "bytes" + "io" + "os" +) + +type MyInt int + +var i int +var j MyInt + +// STOP OMIT + +// Reader is the interface that wraps the basic Read method. +type Reader interface { + Read(p []byte) (n int, err error) +} + +// Writer is the interface that wraps the basic Write method. +type Writer interface { + Write(p []byte) (n int, err error) +} + +// STOP OMIT + +func readers() { // OMIT + var r io.Reader + r = os.Stdin + r = bufio.NewReader(r) + r = new(bytes.Buffer) + // and so on + // STOP OMIT +} + +func typeAssertions() (interface{}, error) { // OMIT + var r io.Reader + tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) + if err != nil { + return nil, err + } + r = tty + // STOP OMIT + var w io.Writer + w = r.(io.Writer) + // STOP OMIT + var empty interface{} + empty = w + // STOP OMIT + return empty, err +} + +func main() { +} diff --git a/doc/progs/interface2.go b/doc/progs/interface2.go new file mode 100644 index 00000000000..e2716cf16d3 --- /dev/null +++ b/doc/progs/interface2.go @@ -0,0 +1,112 @@ +package main + +import ( + "fmt" + "reflect" +) + +func main() { + var x float64 = 3.4 + fmt.Println("type:", reflect.TypeOf(x)) + // STOP OMIT + // TODO(proppy): test output OMIT +} + +// STOP main OMIT + +func f1() { + // START f1 OMIT + var x float64 = 3.4 + v := reflect.ValueOf(x) + fmt.Println("type:", v.Type()) + fmt.Println("kind is float64:", v.Kind() == reflect.Float64) + fmt.Println("value:", v.Float()) + // STOP OMIT +} + +func f2() { + // START f2 OMIT + var x uint8 = 'x' + v := reflect.ValueOf(x) + fmt.Println("type:", v.Type()) // uint8. + fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true. + x = uint8(v.Uint()) // v.Uint returns a uint64. + // STOP OMIT +} + +func f3() { + // START f3 OMIT + type MyInt int + var x MyInt = 7 + v := reflect.ValueOf(x) + // START f3b OMIT + y := v.Interface().(float64) // y will have type float64. + fmt.Println(y) + // START f3c OMIT + fmt.Println(v.Interface()) + // START f3d OMIT + fmt.Printf("value is %7.1e\n", v.Interface()) + // STOP OMIT +} + +func f4() { + // START f4 OMIT + var x float64 = 3.4 + v := reflect.ValueOf(x) + v.SetFloat(7.1) // Error: will panic. + // STOP OMIT +} + +func f5() { + // START f5 OMIT + var x float64 = 3.4 + v := reflect.ValueOf(x) + fmt.Println("settability of v:", v.CanSet()) + // STOP OMIT +} + +func f6() { + // START f6 OMIT + var x float64 = 3.4 + v := reflect.ValueOf(x) + // START f6b OMIT + v.SetFloat(7.1) + // STOP OMIT +} + +func f7() { + // START f7 OMIT + var x float64 = 3.4 + p := reflect.ValueOf(&x) // Note: take the address of x. + fmt.Println("type of p:", p.Type()) + fmt.Println("settability of p:", p.CanSet()) + // START f7b OMIT + v := p.Elem() + fmt.Println("settability of v:", v.CanSet()) + // START f7c OMIT + v.SetFloat(7.1) + fmt.Println(v.Interface()) + fmt.Println(x) + // STOP OMIT +} + +func f8() { + // START f8 OMIT + type T struct { + A int + B string + } + t := T{23, "skidoo"} + s := reflect.ValueOf(&t).Elem() + typeOfT := s.Type() + for i := 0; i < s.NumField(); i++ { + f := s.Field(i) + fmt.Printf("%d: %s %s = %v\n", i, + typeOfT.Field(i).Name, f.Type(), f.Interface()) + } + // START f8b OMIT + s.Field(0).SetInt(77) + s.Field(1).SetString("Sunset Strip") + fmt.Println("t is now", t) + // STOP OMIT +} diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go index 53638a4624b..6356b296df8 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -12,7 +12,7 @@ // for that type. // // See "The Laws of Reflection" for an introduction to reflection in Go: -// http://blog.golang.org/2011/09/laws-of-reflection.html +// http://golang.org/doc/articles/laws_of_reflection.html package reflect import (