Skip to content

Instantly share code, notes, and snippets.

@Integralist
Last active August 19, 2020 09:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Integralist/de9773dc041b2dc3997096817a827d71 to your computer and use it in GitHub Desktop.
Save Integralist/de9773dc041b2dc3997096817a827d71 to your computer and use it in GitHub Desktop.
[Golang Struct and Interface Embedding Examples] #go #golang #struct #embedding #embed

1. simplified 'reduced' examples

This shows struct and interface embedding with basic custom objects to hopefully more easily explain how things work.

See https://gist.github.com/Integralist/b2e87acad7fdf354ade3250dcb31c168#file-1-md for an explanation.

2. real example with gzip.NewWriter

This shows struct and interface embedding with real golang stdlib object.

3. we want to return two different objects (with different methods)

This is so in a 'dev' environment we can return an object that calls a 'mock' method, while in production the returned object will be different and so it won't call that method.

package main
import (
"fmt"
)
type foo struct {
bar string
}
func (f *foo) baz() {
fmt.Println("baz called and bar =", f.bar)
}
type x interface {
baz()
}
/*
// struct embedding example
type bar struct {
*foo
}
*/
// interface embedding example
type bar struct {
x
}
func newBar() *bar {
return &bar{&foo{"abc"}}
// you can also be explicit and specify the 'field', which is x
//
// e.g.
// return &bar{x: &foo{"abc"}}
//
// typically the field is the last segment of the struct/interface
//
// e.g.
// *redis.Client (struct: the field would be Client)
// io.Writer (interface: the field would be Writer)
//
// with either a struct embedded or an interface embedded
// you can call the embedded functions directly AND indirectly
//
// e.g. example where we embedded *redis.Client we can call its TTL() function like so...
// b.TTL()
// b.Client.TTL()
//
// e.g. example where we embedded x interface we can call its baz() function like so...
// b.baz()
// b.x.baz()
}
func doSomething(a x) {
fmt.Println("doSomething called")
a.baz()
}
func main() {
b := newBar()
b.baz()
// b.foo.baz() // only works when a struct is embedded inside a struct, not when an interface is embedded
// b.x.baz() // works when interface is embedded inside a struct as 'x' is the name of the interface
fmt.Printf("%+v\n", b)
doSomething(b)
}
package main
import (
"compress/gzip"
"fmt"
"os"
)
type gzipWriterWrapper struct {
*gzip.Writer // what identifier is this exposed as? see below!
foo string
}
type writerThing struct {
io.Writer
foo string
}
func main() {
g := gzipWriterWrapper{gzip.NewWriter(os.Stdout), "bar"}
fmt.Printf("%+v\n", g) // {Writer:0x448460 foo:bar}
wt := writerThing{foo: "bar"}
fmt.Printf("%+v\n", g) // {Writer:nil foo:bar}
// notice you still need to provide an _implementation_ of io.Writer
}
/*
gzip.Writer methods can be accessed either directly or indirectly...
- indirectly: g.Write(...)
- directly: g.Writer.Write(...)
*/
package main
import (
"fmt"
)
type foo struct {
bar string
}
func (f *foo) baz() {
fmt.Println("baz called and bar =", f.bar)
}
type mock struct {
*bar
}
func (m *mock) mocker() {
fmt.Println("mocker called")
}
// struct embedding example
type bar struct {
*foo
}
type basicRequirements interface {
baz()
}
func newBar(mocked bool) basicRequirements {
if mocked {
fmt.Println("create instance of bar that has mock method")
return &mock{&bar{&foo{"abc"}}}
}
fmt.Println("create instance of bar that does NOT have mock method")
return &bar{&foo{"abc"}}
}
func main() {
b := newBar(true)
b.baz()
// careful if calling newBar(false) as this assertion will break
basserted := b.(*mock)
basserted.mocker()
fmt.Printf("%+v\n", b)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment