Home

Golang#

Go ist eine kompilierte Sprache die von Google entwickelt wurde.

Imports#

Go mag keine relativen Imports. Daher muss immer der absolute Pfad angeben werden.

import (
    // Module befindet sich in ./pkg/loading
	"github.com/hmaier-dev/contacts_converter/pkg/loading"
    // Falsch wäre:
    "../pkg/loading"
)

Ein Underscore stellt eine Blank-Import dar. Das heißt, man selbst greift nicht auf das Module zu, sondern andere Module die man importiert hat. Man sieht dies bspw. beim Import eines SQLite-Drivers

import (
    "database/sql"
    _ "github.com/mattn/go-sqlite3" // needed for database/sql when working with sqlite3
)

Context#

The context-package is used to manage cancellation-signals, deadline and request-scoped values.

package main
import (
    "context"
    "fmt"
    "time"
)

func main() {
    // Create a parent context
    parent := context.Background()

    // Create a child context with a deadline set to 100 ms 
    ctx, cancel := context.WithDeadline(parent, time.Now().Add(100*time.Millisecond))
    defer cancel() // Make sure to call cancel to release resources

    select {
    case <-time.After(200 * time.Millisecond):
        fmt.Println("Operation completed") // this won't be reached
    case <-ctx.Done():
        fmt.Println("Operation canceled due to deadline") // this will be reached
    }
}

Go-Routines#

Goroutines make concurrency possible. That means, running two seperate function seperatly without dependency between them. Different goroutines communicate via channels.

package main

import (
    "fmt"
    "time"
)

func count(name string) {
    for i := 1; i <= 5; i++ {
        fmt.Println(name, ":", i)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go count("goroutine")
    count("main function")
    time.Sleep(1 * time.Second)
}

Channels#

They allow safe data exchange and data synchronization between goroutines without shared memory access.

Schlafende Go-Routine neustarten#

Ich bin heute auf den Fall gestoßen, eine schlafende Go-Routine neustarten zu müssen. Da man time.Sleep(d) nicht unterbrechen, bzw. nicht zum Aufwecken zwingen kann, benutzt man daher time.After(d).

time.After(d) macht einen Channel auf, den man zusammen mit dem Channel, über welchen man sein Signal senden möchte, an ein select-Statement andockt. select hört nun beide Channels ab und führt bei eintreffen eines Signals, den jeweiligen Logik-Block aus.


var restartSignal = make(chan bool)
func main() {
    go maintain()
    for{
        time.Sleep(time.Second * 7)
        restartSignal <- true
        fmt.Println("Lets restart!")
        close(restartSignal) // alten Channel schließen
        restartSignal = make(chan bool) //neuen Channel öffnen
        go maintain()
    }
}
func maintain() {
    count := 0
    for{
        count++
        fmt.Printf("Iteration.. %d\n", count)
        select{
            case <-restartSignal:
                return // Funktion beenden
            case <-time.After(time.Second * 3):
                break // Aus select ausbrechen und nächste Iteration einleiten
        }
    }
}

Modules forken#

Hat man ein Module was nicht so funktioniert, wie man es möchte, macht man sich einfach einen Fork davon. Nun hat man die Macht Änderungen um Code vorzunehmen. Sollen diese Änderungen direkt im Hauptprojekt nutzbar sein, kann man das geforkte Repository klonen und Go dazu bringen, dies zu nutzen. Dies tut man in der go.mod mit dem replace-Kommando.

require (
	github.com/emersion/go-vcard v0.0.0-00010101000000-000000000000
)

replace github.com/emersion/go-vcard => ../go-vcard

Den Pfad des geforkten Repositorys muss man von den root des Hauptprojekts angeben.

Datenbanken#

Get Everything#

    db, err := sql.Open("sqlite3", source)
    if err != nil{
        fmt.Println(err)
    }
    rows, err := db.Query("Select * from table;")
    if err != nil{
        log.Fatalf("%#v\n", err)
    } 
    cols, err := rows.Columns()

    if err != nil{
        log.Fatalf("%#v\n", err)
    } 
	rawResult := make([][]byte, len(cols)) // [row][values] -> e.g. row: [[value][value][value]]
	dest := make([]interface{}, len(cols)) // .Scan() needs []any as result type
	allRows := make([][]string, 0)
	for i := range cols {
		dest[i] = &rawResult[i] // mapping dest indices to byte slice
	}
	for rows.Next() {
		err := rows.Scan(dest...)
		if err != nil {
			log.Fatal("problems scanning the database", err)
		}
		singleRow := make([]string, len(cols))
		for i, raw := range rawResult {
			singleRow[i] = string(raw) // from byte to string
			//fmt.Printf("%v -> %v \n", i, singleRow)
		}
		allRows = append(allRows, singleRow)
	}
    
    fmt.Printf("%v\n", allRows)

Commandline Arguments#

If you need something quick, without a variable info, just use this snippet:

if len(os.Args) > 1{
    for _ , s := range os.Args[1:]{ // index 0 is the name of the program, so slice it away
        switch s {
        case "--exporter":
            db.Export()
        default:
            fmt.Printf("Argument unknown: %s \n", s)
            os.Exit(0)
        }
    }

}

For more complex stuff, use the flag-package.