Daksh Pareek

Welcome to my personal portfolio website, showcasing my projects and blogs.

Go Learning Journal 1: Aliases and Embedding

2025-02-04


These are raw learning notes from my journey with Go, documenting key concepts and examples for future reference. I reason with LLMs to understand some concepts better and sometimes copy paste the responses of that in my notes. These are not original thoughts.

Alias in Go

An alias is a new name for a type. Alias is declared using the keyword type followed by the alias name and the type name.

type Image struct {
	id int
	name string
}

type Photo = Image
var photo := Photo{123, "desktop.png"}
var img Img = photo

Package-level Variables:

package mypackage

// Exported variable (starts with capital letter)
var MyVariable = 42  // This name cannot have an alternate name

Struct Fields:

package mypackage

type Person struct {
    Name string    // This exported field name cannot have an alternate name
    Age  int      // This exported field name cannot have an alternate name
}


go run file

The go run command builds the binary, executes the binary from that temporary directory, and then deletes the binary after your program finishes.


  1. What is Embedding?
  1. Why use Embedding?
  1. Difference from Regular File Operations:
// Regular File Reading (Traditional way)
data, err := os.ReadFile("config.txt")  // Reads from disk
// File must exist on the computer

// Embedding (New way)
//go:embed config.txt
var configData string  // File is part of the program
// No need for external file
  1. Simple Example:
package main

import (
    _ "embed"
    "fmt"
)

//go:embed hello.txt
var content string

func main() {
    // content already contains the text from hello.txt
    fmt.Println(content)
}
  1. Real-world Examples:
  1. Types of Embedding:
// Single file as string
//go:embed file.txt
var data string

// Single file as bytes
//go:embed file.bin
var data []byte

// Multiple files or directories
//go:embed templates/*
var files embed.FS
  1. Key Benefits:

Think of embedding like creating a zip file that’s permanently part of your program, rather than keeping files separate on disk.

Example of why it’s useful:

// Without embedding
MyApp/
  ├── myapp (executable)
  ├── templates/
  ├── config.txt
  └── data.json

// With embedding
MyApp/
  └── myapp (executable with everything inside it)

This makes distribution and deployment much simpler and more reliable.

Approach 1: Using File System (Traditional)

// main.go
package main

import (
    "fmt"
    "os"
)

func main() {
    // Reads quotes.txt from disk
    data, err := os.ReadFile("quotes.txt")
    if err != nil {
        fmt.Println("Error: quotes.txt must be in same directory!")
        return
    }
    fmt.Println(string(data))
}

Directory structure:

myproject/
├── main.go
└── quotes.txt

After build:
myproject/
├── main     (binary/executable)
└── quotes.txt

You need to distribute BOTH files - the binary and quotes.txt

Approach 2: Using Embedding

// main.go
package main

import (
    _ "embed"
    "fmt"
)

//go:embed quotes.txt
var quotes string

func main() {
    // quotes already contains the content of quotes.txt
    fmt.Println(quotes)
}

Directory structure after build:

myproject/
└── main     (binary/executable with quotes.txt content inside it)

You only need to distribute the binary file - it contains everything!

Key differences:

  1. With filesystem approach:

    • If quotes.txt is missing, program fails
    • Need to distribute both files
    • Files can be accidentally modified/deleted
  2. With embedding:

    • Everything is in one file
    • Can’t lose the quotes file
    • Simpler distribution
    • Files can’t be modified (they’re part of the program)

This is particularly useful when distributing your program to others or deploying to servers - you don’t have to worry about managing multiple files!

Best Practices for Using Embedding:

  1. Use embedding for:

    • Default configurations
    • Static assets
    • Templates
    • Documentation
    • Default resources
  2. Consider filesystem for:

    • User-specific configurations
    • Dynamic content
    • Files that need runtime modifications
    • Large files that might need updates
  3. Hybrid Approach:

    • Embed defaults
    • Allow filesystem overrides
    • This gives flexibility while maintaining reliability