Skip to content

Instantly share code, notes, and snippets.

@jamesharr
Last active January 2, 2016 16:09
Show Gist options
  • Save jamesharr/8327891 to your computer and use it in GitHub Desktop.
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.
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)
}
}
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
}
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