Last active
January 2, 2016 16:09
-
-
Save jamesharr/8327891 to your computer and use it in GitHub Desktop.
Get around Go's lack of type parameterization with pointers. It's about a 40% performance hit to do it with reflection, but that's 900ns vs 500ns, so as long as a majority of your work doesn't involve fetching things this way, it's a pretty effective way to services in Go.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
. "play/fill" | |
) | |
func main() { | |
var a Foo // Foo is just an interface, not a concrete type. | |
// Normal operation | |
if err := Fill(&a); err == nil { | |
fmt.Println(a.Say("Hello")) | |
} else { | |
fmt.Println("ERR", err) | |
} | |
// Boring method. Notice we have to specify the service we want via parameter. | |
if svc, err := GetFoo("foo"); err == nil { | |
if svc, ok := svc.(Foo); ok { | |
fmt.Println(svc.Say("Hello")) | |
} | |
} else { | |
fmt.Println("ERR", err) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package fill | |
import ( | |
"fmt" | |
"reflect" | |
) | |
// Our interface | |
type Foo interface { | |
Say(string) string | |
} | |
// Our concrete type | |
type FooImpl struct { | |
Name string | |
} | |
func (f FooImpl) Say(msg string) string { | |
return fmt.Sprintf("%s, %s", msg, f.Name) | |
} | |
// Some other type that FooImpl Doesn't implement | |
type Bar interface { | |
OtherMethod() | |
} | |
// Fill(&someIfaceVar) will put an implementation into a particular interface | |
func Fill(iface interface{}) error { | |
// Instance created/found through some magical method. Likely based on the type iface points to. | |
var myInstance FooImpl | |
myInstance = FooImpl{"World"} | |
myInstanceValue := reflect.ValueOf(myInstance) | |
myInstanceType := reflect.TypeOf(myInstance) | |
// Get the pointer the API user passed to us | |
ifacePtrValue := reflect.ValueOf(iface) | |
if ifacePtrValue.Kind() != reflect.Ptr { | |
return fmt.Errorf("value is not a pointer: %v", ifacePtrValue.Kind()) | |
} | |
// Get the value of the thing at that pointer | |
ifaceValue := reflect.Indirect(ifacePtrValue) | |
ifaceType := ifaceValue.Type() | |
if !myInstanceType.AssignableTo(ifaceType) { | |
return fmt.Errorf("%v is not assignable to %v", myInstanceType, ifaceType) | |
} | |
// Set | |
ifaceValue.Set(myInstanceValue) | |
return nil | |
} | |
func GetFoo(svcName string) (interface{}, error) { | |
if svcName != "foo" { | |
return nil, fmt.Errorf("Service not found") | |
} | |
return FooImpl{"World"}, nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package fill_test | |
import ( | |
"play/fill" | |
"testing" | |
) | |
var result string | |
func BenchmarkReflectFill(b *testing.B) { | |
var r string | |
for i := 0; i < b.N; i++ { | |
var f fill.Foo | |
if err := fill.Fill(&f); err == nil { | |
r = f.Say("Hello") | |
} else { | |
panic("Fuck") | |
} | |
} | |
result = r | |
} | |
func BenchmarkAssign(b *testing.B) { | |
var r string | |
svcType := "foo" | |
for i := 0; i < b.N; i++ { | |
if f, err := fill.GetFoo(svcType); err == nil { | |
f := f.(fill.Foo) | |
r = f.Say("Hello") | |
} else { | |
panic("Fuck") | |
} | |
} | |
result = r | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment