Skip to content

Instantly share code, notes, and snippets.

@tsnow
Created September 15, 2014 05:26
Show Gist options
  • Save tsnow/2b5b3a0d13f4e43d8a46 to your computer and use it in GitHub Desktop.
Save tsnow/2b5b3a0d13f4e43d8a46 to your computer and use it in GitHub Desktop.

FROM @anachronistic:

BTW, in case it's not obvious (because after some talking with people it's definitely not obvious, and it wasn't obvious to me in the Go code I wrote months back), embedded types are more or less extend in Go (that is to say, they are the way to composition).

ex. I have a BlogPost struct that records the number of views a particular post has received. I realize that my RecordView method is not threadsafe, so I decide I want to lock a mutex, then update, then unlock (yes, atomic update is faster, but I'm really just proving a structural point here).

type BlogPost struct {
    ID            int
    Views         int
    Visible       bool
    AllowComments bool
}

func (post *BlogPost) RecordView() {
    post.Views++
}

I could add a field to my struct called _mutex (or whatever) and then call post._mutex.Lock() and post._mutex.Unlock() in my method.

type BlogPost struct {
    ID            int
    Views         int
    Visible       bool
    AllowComments bool

    _mutex sync.Mutex
}

func (post *BlogPost) RecordView() {
    post._mutex.Lock()
    post.Views++
    post._mutex.Unlock()
}

That's for chumps. Embed dat shit homie, and extend BlogPost with the requisite methods.

type BlogPost struct {
    ID            int
    Views         int
    Visible       bool
    AllowComments bool

    sync.Mutex
}

func (post *BlogPost) RecordView() {
    post.Lock()
    post.Views++
    post.Unlock()
}

I'll leave the "What happens if you embed multiple types that all implement the same interface?" question as an exercise for the reader.

// EDITORS COMMENT: long flights were made for playing with Go's type system semantics. - @tsnow
package main
import (
"fmt"
"reflect"
"unsafe"
)
type Fart struct {
vespeneGas string
}
type Puppy struct {
Fart
}
type Kitten struct {
Fart
}
type Basket struct {
Puppy
Kitten
}
func (k Kitten) says() {
fmt.Println("Meow dammit.")
}
func (k Kitten) jumps() {
fmt.Println("Boing!")
}
func (p Puppy) says() {
fmt.Println("YAY Bark!")
}
func (p Puppy) begs() {
fmt.Println("Such food. Much can has. Wow.")
}
func (p *Puppy) nomKibbles() {
p.vespeneGas = "kibbles";
}
func (p *Puppy) poot() {
fmt.Println(p.vespeneGas);
}
func (k *Kitten) canHasCheezburger(){
k.vespeneGas = "cheezburger";
}
func (k *Kitten) fluff(){
fmt.Println(k.vespeneGas);
}
func main() {
var pup = Puppy{};
var kitteh = Kitten{};
var basket = Basket{};
pup.says()
kitteh.says()
kitteh.jumps()
pup.begs()
basket.jumps()
basket.begs()
/*
Uniqueness of identifiers
Given a set of identifiers, an identifier is called unique if it is different from every other in the set. Two identifiers are different if they are spelled differently, or if they appear in different packages and are not exported. Otherwise, they are the same.
*/
// basket.says() // ./sandbox.go:46: ambiguous selector basket.says
// basket.vespeneGas = "blah" // ./sandbox.go:52: ambiguous selector basket.vespeneGas
fmt.Println(unsafe.Sizeof(pup))
fmt.Println(unsafe.Sizeof(basket))
fmt.Println(reflect.TypeOf(pup).Field(0))
fmt.Println(reflect.TypeOf(basket).Field(0))
fmt.Println(reflect.TypeOf(basket).Field(1))
basket.nomKibbles()
basket.poot()
basket.fluff()
basket.canHasCheezburger()
basket.fluff()
basket.poot()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment