Share & grow the world's code base!

Delve into a community where programmers unite to discover code snippets, exchange skills, and enhance their programming proficiency. With abundant resources and a supportive community, you'll find everything essential for your growth and success.

35 snippets
  • Buffered and unbuffered channels in Go

    package main
    
    import "fmt"
    
    func main() {
        // Create unbuffered channel
    	unbufferedChannel := make(chan string)
    
    	go func() {
            // Send a value into a channel using the channel<- syntax
    		unbufferedChannel <- "unbuffered channel"
    	}()
    
        // The <-channel syntax receives a value from the channel
    	fmt.Println(<-unbufferedChannel)
    
        // Create buffered channel with size 2
    	bufferedChannel := make(chan string, 2)
    
        // Send a value to a buffered channel. The operation in non blocking
    	bufferedChannel <- "buffered"
    	bufferedChannel <- "channel"
    
    	fmt.Println(<-bufferedChannel)
    	fmt.Println(<-bufferedChannel)
    }
    
    // go run main.go 
    // unbuffered channel
    // buffered
    // channel

    Channels serve as pipes for communication between concurrent goroutines. They allow one goroutine to send values and another goroutine to receive those values. By default, sending and receiving operations block until both the sender and receiver are ready. This feature enabled us to wait for the "unbuffered channel" message at the end of our program without needing any additional synchronization mechanisms. Buffered channels can hold a limited number of values without requiring an immediate receiver.

  • Environment variables in Go

    package main
    
    import (
    	"fmt"
    	"os"
    	"strings"
    )
    
    func main() {
    	os.Setenv("FOO", "1")
    	fmt.Println("FOO:", os.Getenv("FOO"))
    	fmt.Println("BAR:", os.Getenv("BAR"))
    
    	fmt.Println("----------------------")
    
    	for _, env := range os.Environ() {
    		pair := strings.SplitN(env, "=", 2)
    		fmt.Println(pair[0])
    	}
    }
    
    // FOO: 1
    // BAR: 
    // ----------------------
    // HOSTNAME
    // PWD
    // HOME
    // LANG
    // SHLVL
    // PATH
    // _
    // FOO

    Here's an example how you can manage environment variables. To assign a value to a key, utilize os.Setenv. Retrieve a value by key using os.Getenv, which will yield an empty string if the key isn't found. Use os.Environ to list all key/value pairs, returned as a string slice formatted as KEY=value. You can split these strings using strings.SplitN to separate keys and values.

  • Example of recursion in golang

    package main
    
    import "fmt"
    
    func factorial(n int) int {
    	if n == 0 {
    		return 1
    	}
    
    	return n * factorial(n-1)
    }
    
    func main() {
    	fmt.Println(factorial(5))
    }
    
    // $ go run main.go 
    // 120

    Recursion in Go using the example of factorial calculation.

  • Example of closures in Golang

    package main
    
    import "fmt"
    
    // The function createCounter returns anonymous function defined within its body.
    // The returned function encapsulates the variable count, creating a closure.
    func createCounter() func() int {
    	count := 0
    
    	return func() int {
    		count++
    
    		return count
    	}
    }
    
    func main() {
        // We invoke createCounter, storing the result (a function) in counter variable.
        // This function captures and retains its own count value,
        // which updates with each subsequent invocation of createCounter.
    	counter := createCounter()
    
    	fmt.Println(counter())
    	fmt.Println(counter())
    	fmt.Println(counter())
    
    	newCounter := createCounter()
    	
        fmt.Println(newCounter())
    }
    
    // $ go run main.go 
    // 1
    // 2
    // 3
    // 1

    A simple example of using closures in Go.

  • Detect OS in Go

    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    func main() {
        // The runtime.GOOS constant can be used to detect the OS at runtime,
        // since this constant is only set at runtime.
        os := runtime.GOOS
    
        switch os {
        case "windows":
            fmt.Println("Windows")
        case "darwin":
            fmt.Println("MacOS")
        case "linux":
            fmt.Println("Linux")
        default:
            fmt.Printf("%s.\n", os)
        }
    
        // The runtime.GOARCH constant can be used to determine the target architecture of a running program.
        fmt.Println(runtime.GOARCH)
    }
    
    // Output:
    // Linux
    // amd64

    GOOS constant to determine the operating system your Go program is running on. Here's an example of how to check the operating system in Go.

  • Create temporary file or directory in Go

    package main
    
    import (
        "fmt"
        "os"
        "path/filepath"
    )
    
    func checkErr(err error) {
        if err != nil {
            panic(err)
        }
    }
    
    func main() {
        // The simplest way to create a temporary file is to call os.CreateTemp.
        // It will create and open the file for reading and writing.
        // We used "" as the first argument, so os.CreateTemp will create a file in the default directory.
        tmpFile, err := os.CreateTemp("", "tmpfile")
        checkErr(err)
        defer os.Remove(tmpFile.Name())
    
        fmt.Println("Temp file name:", tmpFile.Name())
    
        // Write some data to the temporary file
        _, err = tmpFile.Write([]byte{1, 2, 3, 4, 5})
        checkErr(err)
    
        // If we intend to write a lot of temporary files, we may prefer to create a temporary directory.
        // The arguments to os.MkdirTemp are the same as for os.CreateTemp, but it returns the directory name rather than the opened file.
        dName, err := os.MkdirTemp("", "tmpdir")
        checkErr(err)
        defer os.RemoveAll(dName)
        
        fmt.Println("Temp directory name:", dName)
    
        fName := filepath.Join(dName, "testFile")
        err = os.WriteFile(fName, []byte{1, 2, 3, 4, 5}, 0666)
        checkErr(err)
        defer os.Remove(fName)
    }
    
    // Output:
    // Temp file name: /tmp/tmpfile3400905374
    // Temp directory name: /tmp/tmpdir2812568099

    During program execution, we often want to create data that is not needed after the program exits. Temporary files and directories are useful for this purpose because they do not pollute the file system over time.

  • Writing files in Go

    package main
    
    import (
        "bufio"
        "fmt"
        "os"
    )
    
    func checkErr(err error) {
        if err != nil {
            panic(err)
        }
    }
    
    func main() {
        content := "Some content"
        // write a string (or just bytes) into a file
        err := os.WriteFile("/tmp/dfile1", []byte(content), 0644)
        checkErr(err)
    
        // open a file for writing
        file, err := os.Create("/tmp/dfile2")
        checkErr(err)
        // defer a Close immediately after opening a file
        defer file.Close()
    
        bytesCount, err := file.Write([]byte(content))
        checkErr(err)
        fmt.Printf("wrote %d bytes\n", bytesCount)
    
        bytesCount, err = file.WriteString("Some other content\n")
        checkErr(err)
        fmt.Printf("wrote %d bytes\n", bytesCount)
        // flush writes to a stable storage (file system for example)
        file.Sync()
    
        // bufio provides buffered writers
        writer := bufio.NewWriter(file)
        bytesCount, err = writer.WriteString("Some othe buffered content\n")
        checkErr(err)
        fmt.Printf("wrote %d bytes\n", bytesCount)
        // ensure all buffered operations have been applied to the underlying writer
        writer.Flush()
    }

    Here are examples of writing data to files using Go.

  • Generate random hash or token in Go

    package main
    
    import (
    	"crypto/rand"
    	"crypto/sha256"
    	"encoding/hex"
    	"fmt"
    )
    
    func GenerateRandomHash(n int) (string, error) {
    	b := make([]byte, n)
    	_, err := rand.Read(b)
    
    	// Note that err == nil only if we read len(b) bytes.
    	if err != nil {
    		return "", err
    	}
    
    	hasher := sha256.New()
    	hasher.Write(b)
    	sha := hex.EncodeToString(hasher.Sum(nil))
    
    	return sha, nil
    }
    
    func main() {
    	hash, err := GenerateRandomHash(512)
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(hash)
    }
    
    // go run main.go 
    // 34c0fb393623843e56719b5d9d66385a55b4b4d3393187b7b1a76aee46c421c5

    Here is an example of randomly generating a hash or token in Go.

  • SHA256 hashes in Go

    package main
    
    import (
        "crypto/sha256"
        "fmt"
    )
    
    func main() {
        str := "some string to calculate hash"
        hash := sha256.New()
    
        hash.Write([]byte(str))
        sum := hash.Sum(nil) // the argument can be used to append to an existing byte slice
    
        fmt.Println(str)
        fmt.Printf("%x\n", sum)
    }
    
    // go run main.go 
    // some string to calculate hash
    // 7aad383b9ad516fa67057adc283ce2cf71858aff317a5e267adebfdbd5dda5fd

    SHA256 hashes are commonly used to generate short identifiers for binary or text data. This example shows how to calculate SHA256 hashes for string in Go.

  • Example of using mutexes in Go

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    // without using mutex the following error can happen: "fatal error: concurrent map writes" while changing the map in multiple goroutines
    type Storage struct {
    	sync.Mutex
    	storage map[string]int // map is not threade-safe
    }
    
    func (s *Storage) Increment(name string) {
    	s.Lock() // lock the mutex before accessing counters
    	defer s.Unlock() // unlock the mutex at the end of the function using a defer statement
        s.storage[name]++
    }
    
    func (s *Storage) Decrement(name string) {
    	s.Lock()
        defer s.Unlock()
    	s.storage[name]--
    }
    
    func main() {
    	s := &Storage{
    		storage: make(map[string]int),
    	}
    
    	wg := sync.WaitGroup{}
    	wg.Add(5)
    
        // increment a named counter in a loop
    	increment := func(name string, count uint) {
    		for i := 0; i < int(count); i++ {
    			s.Increment(name)
    		}
    
    		wg.Done()
    	}
    
        // decrement a named counter in a loop
    	decrement := func(name string, count uint) {
    		for i := 0; i < int(count); i++ {
    			s.Decrement(name)
    		}
    
    		wg.Done()
    	}
    
        // run 5 goroutines concurrently
    	go increment("a", 1000)
    	go decrement("a", 500)
    
    	go increment("b", 1000)
    	go increment("b", 1000)
    	go decrement("b", 1500)
    
    	wg.Wait() // wait for the goroutines to finish
    
    	fmt.Println(s.storage)
    }
    
    // $ go run main.go  
    // map[a:500 b:500]
    
    // without mutex the following error can happen
    // $ go run main.go
    // fatal error: concurrent map writes

    Mutexes can be used to safely access data across multiple goroutines.This example shows how to use mutexes in Go to change a map concurrently and safely.

  • Handle OS signals in Go

    package main
    
    import (
        "fmt"
        "os"
        "os/signal"
        "syscall"
    )
    
    func main() {
        sigs := make(chan os.Signal, 1) // create channel for signal, it should be buffered
        signal.Notify(sigs, syscall.SIGINT) // register the channel to receive notifications of the specified signals
        done := make(chan bool, 1)
    
        go func() {
            sig := <-sigs // wait for OS signal, once received, notify main go routine
            fmt.Printf("\nos signal: %v\n", sig)
            done <- true
        }()
    
        fmt.Println("awaiting signal")
        <-done // wait for the expected signal and then exit
        fmt.Println("exiting")
    }
    
    // $ go run main.go 
    // awaiting os signal
    // ^C
    // os signal: interrupt
    // exiting program

    Here is an example go program for processing Unix signals using channels. Signal processing can be useful, for example, for correct terminating of program when receiving SIGTINT.

  • Worker pools in Go

    package main
    
    import (
        "fmt"
        "time"
    )
    
    // this is a worker that we will run in several parallel instances
    // these workers will receive tasks through the 'jobs' channel and send the results to 'results'
    // we will wait for one second for each task to simulate heavy requests
    func worker(id int, jobs <-chan int, results chan<- int) {
        for j := range jobs {
            fmt.Println("worker", id, "started job", j)
            time.Sleep(time.Second)
            fmt.Println("worker", id, "finished job", j)
            results <- j * 2
        }
    }
    
    func main() {
        // to use worker pool, we need to send a task and receive the execution results
        // therefore 2 channels are created
        jobs := make(chan int, 100)
        results := make(chan int, 100)
    
        // start 3 workers, initially blocked because no assignments yet
        for w := 1; w <= 3; w++ {
            go worker(w, jobs, results)
        }
    
        // send 5 jobs and then close the channel, notofying that all jobs have been sent
        for j := 1; j <= 5; j++ {
            jobs <- j
        }
    
        close(jobs)
    
        // collect all the results
        // this also ensures that the goroutines have ended
        for a := 1; a <= 5; a++ {
            <-results
        }
    }

    This example shows how to implement a worker pool using channels and goroutines.

  • Channel synchronization in Go

    package main
    
    import (
        "fmt"
        "time"
    )
    
    // the channel is used to notify main goroutine that the function completed successfully
    func worker(done chan bool) {
        fmt.Println("working...")
        time.Sleep(time.Second)
        fmt.Println("done")
    
        done <- true // send a value to indicate that the function completed successfully
    }
    
    func main() {
        done := make(chan bool, 1) // create a buffered channel for notification
        go worker(done) // run worker
    
        <-done // blocked until a notification is received from the worker from the channel
               // if you remove the line <- done the program will close before the worker starts
    }

    Channels can be used to synchronize execution between goroutines. Here is an example of using a channel to wait for a goroutine to complete.

  • Example of timeouts in Go

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	first := make(chan string, 1)
    
    	go func() {
    		time.Sleep(2 * time.Second) // wait 2 seconds before send value to channel
    		first <- "first result"     // send value to the channel
    	}()
    
    	select {
    	case result := <-first:
    		fmt.Println(result)
    	case <-time.After(time.Second): // timeout occurs before the value is read from the first channel
    		fmt.Println("first timeout")
    	}
    
    	second := make(chan string, 1)
    
    	go func() {
    		time.Sleep(time.Second)   // wait 1 second before send value to the channel
    		second <- "second result" // send value to the channel
    	}()
    
    	select {
    	case result := <-second: // the value from the channel is read before the timeout expires
    		fmt.Println(result)
    	case <-time.After(2 * time.Second):
    		fmt.Println("second timeout")
    	}
    }
    
    // $ go run timeouts.go 
    // first timeout
    // second result

    Timeouts are important for programs that connect to external resources or need to limit their execution time.

  • Example of using Go tickers

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	ticker := time.NewTicker(time.Second)
    	fmt.Println("ticker is started")
    
    	done := make(chan struct{})
    
    	go func() {
    		for {
    			select {
    			case <-done:
    				return
    			case t := <-ticker.C:
    				fmt.Println("tick", t)
    			}
    		}
    	}()
    
    	time.Sleep(5 * time.Second)
    
    	ticker.Stop()
    	fmt.Println("ticker is stopped")
    
    	time.Sleep(5 * time.Second)
    	done <- struct{}{}
    }
    
    // go run tickers.go 
    // ticker is started
    // tick 2024-02-09 23:40:25.809616086 +0700 +07 m=+1.000091237
    // tick 2024-02-09 23:40:26.809742343 +0700 +07 m=+2.000152938
    // tick 2024-02-09 23:40:27.809804037 +0700 +07 m=+3.000214622
    // tick 2024-02-09 23:40:28.809852444 +0700 +07 m=+4.000263029
    // ticker is stopped
    // tick 2024-02-09 23:40:29.809908932 +0700 +07 m=+5.000319557
    

    Tickers allow you to repeat actions at certain intervals. A Ticker holds a channel that delivers "ticks" of a clock at intervals. Tickers can be stopped in the same way as timers. When the ticker is stopped, it will no longer be able to accept values ​​into its channel.