Skip to content

Instantly share code, notes, and snippets.

@keegoo
Last active April 3, 2025 04:03
Show Gist options
  • Save keegoo/00688540048db106b138da10e2cff316 to your computer and use it in GitHub Desktop.
Save keegoo/00688540048db106b138da10e2cff316 to your computer and use it in GitHub Desktop.
a cheatsheet for go lang

Type

package main
import "fmt"

func main() {

    myString := "dog"
    myByte := []byte("cat")
    var myInt   int = 123456876   // The int, unit and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems.
    var myInt8  int8 = 10
    var myInt32 int32 = 123456789
    var myInt64 int64 = 34567898765434567
    myPString := &myString
    myPInt := &myInt 
    myPByte := &myByte
    myPInt8 := &myInt8
    myPInt32 := &myInt32
    myPInt64 := &myInt64
  
    fmt.Println(getType(myInt))     // int
    fmt.Println(getType(myString))  // string
    fmt.Println(getType(myByte))    // []uint8
    fmt.Println(getType(myInt8))    // int8
    fmt.Println(getType(myInt32))   // int32
    fmt.Println(getType(myInt64))   // int64

    fmt.Println(getType(myPInt))    // *int
    fmt.Println(getType(myPString)) // *string
    fmt.Println(getType(myPByte))   // *[]uint8
    fmt.Println(getType(myPInt8))   // *int8
    fmt.Println(getType(myPInt32))  // *int32
    fmt.Println(getType(myPInt64))  // *int64
}


func getType(anything interface{}) (string) {
    var s string
    switch anything.(type) {
        case []byte:  s = "[]byte"
        case string:  s = "string"
        case int:     s = "int"
        case int8:    s = "int8"
        case int32:   s = "int32"
        case int64:   s = "int64"
        // pointer
        case *[]byte: s = "*[]byte"
        case *string: s = "*string"
        case *int:    s = "*int"
        case *int8:   s = "*int8"
        case *int32:  s = "*int32"
        case *int64:  s = "*int64"
        default:      s = "unknow"
    }
    return s
}

Map

There's some key differences between Map and Struct:

  • Map is dynamic, you can add/remove keys at runtime. While Struct is fixed.
  • Map is using hashing under the hood, so it needs more memory than Struct, and also a bit more slower.
  • someMap["keyname"] instead of someStruct.keyname.
  • Map is iterable, while Struct is NOT by default.
package main

import "fmt"

type Person struct {
    Age  int
    City string
}

func main() {
    // 1. Simple map: Set and Get value
    fruitPrices := map[string]float64{
        "apple":  1.99,
        "banana": 0.99,
    }

    fmt.Println("Price of apple:", fruitPrices["apple"])

    // 2. Map with complex data type (struct)
    people := map[string]Person{
        "Alice": {Age: 30, City: "New York"},
        "Bob":   {Age: 25, City: "San Francisco"},
    }

    // Get a value and check if key exists
    if person, exists := people["Alice"]; exists {
        fmt.Println("Alice's age:", person.Age, ", City:", person.City)
    } else {
        fmt.Println("Alice not found")
    }

    // 3. Iterate over a map
    for name, person := range people {
        fmt.Println("Name:", name, "Age:", person.Age, "City:", person.City)
    }
}

Struct and Method

package main
import "fmt"

type MyStruct struct {
    Name string
}

func (m MyStruct) ValueMethod() {
    m.Name = "Changed in ValueMethod"
    fmt.Println("Inside ValueMethod: ", m.Name)
}

func (m *MyStruct) PointerMethod() {
    m.Name = "Changed in PointerMethod"
    fmt.Println("Inside PointerMethod: ", m.Name)
}

func main() {
    example := MyStruct{Name: "Original"}
    example.ValueMethod()                               // Inside ValueMethod: Changed in ValueMethod
    fmt.Println("After ValueMethod: ", example.Name)    // After ValueMethod: Original
  
    example.PointerMethod()                             // Inside ValueMethod: Changed in ValueMethod
    fmt.Println("After PointerMethod: ", example.Name)  // Inside ValueMethod: Changed in ValueMethod
}

Interface

In Go, interfaces are the way to implement duck typing.

package main

import "fmt"

// Define an interface for behavior
type Quacker interface {
    Quack() string
}

// Define a struct that implements the interface
type Duck struct {
    Name string
}
func (d Duck) Quack() string {
    return d.Name + " quacks like a duck!"
}

// Define another struct that "quacks"
type Dog struct {
    Name string
}
func (d Dog) Quack() string {
    return d.Name + " quacks like a dog!"
}

// Function that accepts any type that satisfies the Quacker interface
func describeQuacking(quacker Quacker) {
    fmt.Println(quacker.Quack())
}

func main() {
    myDuck := Duck{Name: "Daffy"}
    myDog := Dog{Name: "Buddy"}
  
    describeQuacking(myDuck) // Daffy quacks like a duck!
    describeQuacking(myDog)  // Buddy quacks like a dog!
}

If you come across some code and are wondering whether a certain type is a struct or an interface, you can usually make a distinction by looking at its name.

  • interface in Go are typically named with a verb or an action, representing behaviors or actions that a type should implement. For example, Writer, Reader, and EventRecorder
  • struct, on the other hand, are usually nouns that represent a concrete type or an object. Examples include Event, Client, or AzureReconciler
type AzureReconciler struct {
	client.Client                       // interface
	Log    logr.Logger                  // interface
	Scheme *runtime.Scheme              // struct
	eventRecorder record.EventRecorder  // interface
}
@keegoo
Copy link
Author

keegoo commented Apr 1, 2025

Type Inference

Go automatically detects types based on the assigned value. It helps reduce boilerplate code, making it easier to read and write without sacrificing type safety.

package main

import "fmt"

func main() {
    // Type Inference
    inferredString := "This is inferred as a string"
    inferredInt := 42
    inferredFloat := 3.14

    // Without Type Inference (Explicit Declaration)
    var explicitString string = "This is explicitly declared as a string"
    var explicitInt int = 42
    var explicitFloat float64 = 3.14

    // Print Results
    fmt.Println("Type Inference:")
    fmt.Printf("String: %s\n", inferredString)
    fmt.Printf("Int: %d\n", inferredInt)
    fmt.Printf("Float: %.2f\n\n", inferredFloat)

    fmt.Println("Explicit Typing:")
    fmt.Printf("String: %s\n", explicitString)
    fmt.Printf("Int: %d\n", explicitInt)
    fmt.Printf("Float: %.2f\n", explicitFloat)
}

C does not support type inference. But Rust, C++, Java all support type inference.

@keegoo
Copy link
Author

keegoo commented Apr 1, 2025

Compose

Compose is achieved through interface in go lang. Following is an example.

package main

import "fmt"

// ===== imagine those are third party libraries =====

// ----- -----
// httpClient interface
type httpClient interface {
	GetData() string
}
// Concrete type implementing httpClient interface
type MyHttpClient struct{}
func (m *MyHttpClient) GetData() string {
	return "Fetched data from HTTP client"
}

// ----- -----
// dbClient interface
type dbClient interface {
	SaveData(data string) bool
}
// Concrete type implementing dbClient interface
type MyDbClient struct{}

func (m *MyDbClient) SaveData(data string) bool {
	fmt.Println("Data saved to DB:", data)
	return true
}

// ===== those are your codes, where you would like to reuse the third party libraries =====

// ----- -----
// Now, define a struct that will use all these interfaces
type MyType struct {
	HttpClient httpClient
	DbClient   dbClient
}
// Define a method for MyType that uses the interfaces to perform actions
func (m *MyType) DoSomething() {
	data := m.HttpClient.GetData()
	m.DbClient.SaveData(data)
}

func main() {
	// Create concrete types
	httpClient := &MyHttpClient{}
	dbClient := &MyDbClient{}

	// Create MyType instance and inject the concrete implementations
	myInstance := &MyType{
		HttpClient: httpClient,
		DbClient:   dbClient,
	}

	// Call the method that uses the interfaces
	myInstance.DoSomething()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment