• Go

Filed under:

Notes on the Go programming language.

Syntax & Mechanics

  • Receivers are a way of attaching functions to a type definition.

  • Byte Slice - []byte

    • A more computer friendly way of representing a string (ie; "Hi" -> [72 105]) through ascii chars.

    • Converting to byte slice from a string: []byte("Hi")

  • Testing

    • Testing in go is built into the language;

    • go test looks for files that end in _test.go

  • Arrays vs Slices

    • Slices are used 99% of the time.

    • Arrays are fixed sizes.

    • Slices are like a fancy array.

    • A slice, behind the scenes, creates two data structures.

      1. Something with a pointer to the head of the array, the capacity (how many elements it can hold), and the length - how many items are in the slice.

      2. An internal array that 1. points to.

  • Maps

        // Example map:
        func main() {
            colors := map[string]string{
                "Red": "#ff0000",
                "Blue": "#0000ff",
            }
        }
    
    • Similar to dicts, hashes, pojo's, etc.

    • All keys must be of the same type; all values must be of the same type.

Pointers

An important section, specifically in what data types need to use pointers vs. which don't.

  • Go is a Pass-by-value language

    • data passed into a function and it is copied into new memory

    • ie; passing a struct into a function will copy that stuct and put it into a new place.

  • The & operator performs: "give me the memory address of the value this variable is pointing at"

    • my_var := "foo" -- accessing the memory address would be &my_var

  • With pointers, when you see a * in front of a type (say in a receiver on a method), it's saying /the type should be a pointer to specific type. Ex: if you have a person type:

        func (pointerToPerson *person) doThing(s string) { // *person indicates receiver value should be a pointer which points TO a value of type person
            (*pointerToPerson).firstName = s               // here `*` points to the actual value at the memory address (so it can be updated in place)
        }
    
    • You don't have to explicitly use the & operator to get memory addresses.

      • If a pointer-type is added to a param/receiver, it will implicity work on struct types.

    • Note: slices when passed in to a function, will actually be modified in place:

              func main() {
                  mySlice := []string{"Hi", "Foo"}
                  updateSlice(mySlice)
                  fmt.Println(mySlice)
              }
      
              func updateSlice(s []string) {
                  s[0] = "Bye"
              }
      
    • This is because of the above described mechanics of a slice - when it's getting passed into a function it's being passed-by-value and so, since slices are actually a data stucture (that has a pointer, cap, and length), it's this metadata that is being passed in by value and duplicated in memory.

The table defines reference types and value types

Value TypesReference Types
Intslices
floatmaps
stringchannels
boolpointers
structsfunctions

You may want to use pointers to change value types in functions. You don't need to worry about pointers with Reference types.

Interfaces

  • A big part of the Go language.

  • Interfaces make it easier to re-use code.

  • Interface are implicit; you don't have to write manual code to link types to interfaces; simply, the interface will describe the function and return type and if a function implements it, it will be implicitly linked.

  • An interface is a description of one or more functions, with it's params and return values. Any function of matching name, params, and return values will implement that interface.

type animal interface {
	func speak() string
}

// instead of needing a function for each animal type...
func (b bear) speak() {
	return "rawr"
}

func (c cat) speak() {
	return "meow"
}

// You can instead implement
func speak(a animal) string {
	return a.speak()
}

Additional notes:

  • Structs can have fields on them that are interfaces, in that the field value, so long as it satisfies the given interface, can be of any shape.

  • Interfaces can be composed of multiple interfaces (an interface can be embedded into another.)