- Introduction
- Installation
- Getting Started
- Basic Syntax
- Control Structures
- Functions
- Composite Types
- Methods and Interfaces
- Concurrency
- Error Handling
- Packages and Modules
- Standard Library
- Testing
- Tools and Ecosystem
- Best Practices
- Resources
- License
Go, also known as Golang, is a statically typed, compiled programming language designed by Google. It is known for its simplicity, efficiency, and strong support for concurrent programming. Go is widely used for building scalable web servers, cloud services, and other distributed systems.
- Simplicity: Clean and easy-to-read syntax.
- Performance: Compiled language with efficient memory management.
- Concurrency: Built-in support for concurrent programming with goroutines and channels.
- Robust Standard Library: Extensive libraries for networking, I/O, and more.
- Cross-Platform: Compiles to native binaries for multiple operating systems.
To install Go, follow these steps:
-
Download Go:
- Visit the official Go download page and download the installer for your operating system.
-
Install Go:
- Run the installer and follow the on-screen instructions.
-
Verify Installation:
- Open a terminal and type
go versionto verify that Go is installed correctly.
- Open a terminal and type
Let's start with a simple "Hello, World!" program in Go.
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}- Explanation:
package main: Defines the package name.mainis a special package that indicates an executable program.import "fmt": Imports thefmtpackage, which contains functions for formatted I/O.func main(): The entry point of the program.fmt.Println("Hello, World!"): Prints "Hello, World!" to the console.
Go uses a workspace to manage code. A typical workspace contains three directories at its root:
src: Contains Go source files organized into packages.pkg: Contains package objects.bin: Contains executable commands.
To set up a Go workspace:
- Create a directory for your workspace.
- Set the
GOPATHenvironment variable to point to your workspace directory. - Use
go getto fetch packages and dependencies.
Variables in Go are explicitly declared and used to store data.
var name string = "Gopher"
age := 5- Explanation:
var name string = "Gopher": Declares a variablenameof typestring.age := 5: Short variable declaration, infers the typeint.
Constants are immutable values that are known at compile time.
const Pi = 3.14- Explanation:
const Pi = 3.14: Declares a constantPiwith a value of3.14.
Go supports several basic data types.
var (
a bool // Boolean
b int // Integer
c float64 // Floating point
d string // String
)Go provides various operators for arithmetic, comparison, and logical operations.
sum := 1 + 2
isEqual := (3 == 3)Conditional statements in Go.
if age > 18 {
fmt.Println("Adult")
} else {
fmt.Println("Minor")
}Switch statements for multiple conditions.
switch day {
case "Monday":
fmt.Println("Start of the week")
case "Friday":
fmt.Println("End of the week")
default:
fmt.Println("Midweek")
}The only loop construct in Go.
for i := 0; i < 5; i++ {
fmt.Println(i)
}Iterate over elements in a collection.
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}Functions are first-class citizens in Go.
func add(x int, y int) int {
return x + y
}Functions can return multiple values.
func swap(x, y string) (string, string) {
return y, x
}Functions that accept other functions as arguments or return them.
func applyOperation(x, y int, op func(int, int) int) int {
return op(x, y)
}
result := applyOperation(3, 4, func(a, b int) int {
return a + b
})Arrays are fixed-size sequences of elements, while slices are dynamically-sized.
var arr [5]int
slice := []int{1, 2, 3, 4, 5}Maps are key-value pairs.
ages := map[string]int{
"Alice": 30,
"Bob": 25,
}Structs are custom data types that group fields.
type Person struct {
Name string
Age int
}
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}Methods are functions with a receiver argument.
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}Interfaces define method sets.
type Animal interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string {
return "Woof!"
}Lightweight threads managed by Go.
go func() {
fmt.Println("Running in a goroutine")
}()Channels are used for communication between goroutines.
ch := make(chan int)
go func() { ch <- 42 }()
fmt.Println(<-ch)The select statement lets a goroutine wait on multiple communication operations.
select {
case msg := <-ch1:
fmt.Println("Received", msg)
case ch2 <- 42:
fmt.Println("Sent 42")
default:
fmt.Println("No communication")
}Go uses explicit error handling.
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide by zero")
}
return a / b, nil
}Go organizes code into packages, and modules are collections of related packages.
-
Creating a Package:
- Create a directory with your package name.
- Add Go source files with
package <name>at the top.
-
Using Modules:
- Initialize a module with
go mod init <module-name>. - Manage dependencies with
go get.
- Initialize a module with
Go's standard library provides a rich set of packages for various tasks, including:
fmt: Formatted I/Onet/http: HTTP client and serveros: Operating system functionalityio: Input and output primitivesencoding/json: JSON encoding and decoding
Go includes a built-in testing framework.
import "testing"
func TestAdd(t *testing.T) {
result := add(2, 3)
if result != 5 {
t.Errorf("Expected 5, got %d", result)
}
}- Run tests with
go test.
- Go Modules: Dependency management system.
- GoDoc: Documentation generation tool.
- GoFmt: Code formatting tool.
- GoLint: Linter for Go source code.
- GoVet: Reports suspicious constructs.
- Code Formatting: Use
gofmtto format your code. - Error Handling: Always check for errors.
- Concurrency: Use goroutines and channels wisely.
- Documentation: Comment your code and use
godocfor documentation. - Testing: Write tests for your code.
This project is licensed under the MIT License - see the LICENSE file for details.
PROJECT EXPLANATIONS :
- Introduction
- Installation
- Basic Syntax
- Data Types
- Variables
- Constants
- Control Structures
- Functions
- Packages
- Arrays and Slices
- Maps
- Structs
- Methods
- Interfaces
- Error Handling
- Concurrency
- Testing
- Best Practices
- Resources
Go (also known as Golang) is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Go was designed to be simple, efficient, and easy to learn, making it ideal for building reliable and efficient software. Key features include:
- Fast compilation
- Garbage collection
- Strong typing
- Concurrency support via goroutines and channels
- Simplicity and minimal syntax
- Excellent standard library
- Visit the official Go website at golang.org
- Download the installer for your operating system
- Follow the installation instructions
go versionGo uses a specific workspace structure:
$HOME/go/
├── bin/ # Contains executable binaries
├── pkg/ # Contains package objects
└── src/ # Contains source files
Add the following to your environment variables:
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/binpackage main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}To run:
go run hello.goTo build:
go build hello.go
./helloGo has several built-in data types:
- bool: true or false
- string: Sequence of characters
- int: Integer (depends on platform, 32 or 64 bit)
- int8/16/32/64: Specific size integers
- uint: Unsigned integer
- uint8/16/32/64: Specific size unsigned integers
- float32/64: Floating point numbers
- complex64/128: Complex numbers
- byte: Alias for uint8
- rune: Alias for int32 (Unicode code point)
package main
import "fmt"
func main() {
var (
boolVar bool = true
intVar int = 42
floatVar float64 = 3.14
stringVar string = "Hello"
runeVar rune = 'A'
byteVar byte = 65
)
fmt.Printf("Types and values: %v %T, %v %T, %v %T, %v %T, %v %T, %v %T\n",
boolVar, boolVar,
intVar, intVar,
floatVar, floatVar,
stringVar, stringVar,
runeVar, runeVar,
byteVar, byteVar)
}// Method 1: Declaration with explicit type
var name string = "John"
// Method 2: Type inference
var name = "John"
// Method 3: Short declaration (inside functions only)
name := "John"var a, b, c int = 1, 2, 3
// Or
var (
name string = "John"
age int = 30
isTrue bool = true
)Constants are declared using the const keyword:
const Pi = 3.14159
const (
StatusOK = 200
StatusNotFound = 404
)
// Using iota for enumeration
const (
Monday = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)if x > 0 {
fmt.Println("x is positive")
} else if x < 0 {
fmt.Println("x is negative")
} else {
fmt.Println("x is zero")
}
// If with initialization
if value := getSomeValue(); value < 10 {
fmt.Println("Value is less than 10")
} else {
fmt.Println("Value is 10 or greater")
}// Standard loop
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// While-like loop
i := 0
for i < 5 {
fmt.Println(i)
i++
}
// Infinite loop
for {
// Do something forever
break // Use break to exit
}
// Range-based loop (for arrays, slices, maps, strings)
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}switch day {
case "Monday":
fmt.Println("Start of work week")
case "Friday":
fmt.Println("End of work week")
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Midweek")
}
// Switch without expression (similar to if-else chain)
switch {
case hour < 12:
fmt.Println("Good morning")
case hour < 17:
fmt.Println("Good afternoon")
default:
fmt.Println("Good evening")
}func add(a, b int) int {
return a + b
}func divide(a, b float64) (float64, error) {
if b == 0.0 {
return 0.0, errors.New("cannot divide by zero")
}
return a / b, nil
}func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // Naked return - returns x and y
}func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// Call with: sum(1, 2, 3, 4)// Function as value
add := func(a, b int) int {
return a + b
}
result := add(3, 4)
// Closure
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
// Usage
counter := adder()
fmt.Println(counter(1)) // 1
fmt.Println(counter(2)) // 3
fmt.Println(counter(3)) // 6Defers the execution of a function until the surrounding function returns:
func main() {
defer fmt.Println("World")
fmt.Println("Hello")
}
// Output: Hello\nWorldGo programs are organized into packages. A package is a collection of Go files in the same directory.
// In file math/math.go
package math
func Add(a, b int) int {
return a + b
}
func Multiply(a, b int) int {
return a * b
}package main
import (
"fmt"
"myproject/math"
)
func main() {
result := math.Add(5, 3)
fmt.Println("Result:", result)
}// Declaration
var arr [5]int // An array of 5 integers, initialized to zero values
// Declaration with initialization
arr := [5]int{1, 2, 3, 4, 5}
// Auto-count elements
arr := [...]int{1, 2, 3, 4, 5} // Compiler counts the elementsSlices are more flexible and commonly used than arrays in Go:
// Create a slice
slice := []int{1, 2, 3, 4, 5}
// Create a slice from an array
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // Elements 1, 2, 3 (indices 1 to 3)
// Create a slice with make
slice := make([]int, 5) // len=5, cap=5
slice := make([]int, 3, 5) // len=3, cap=5
// Append to a slice
slice = append(slice, 6, 7, 8)
// Append one slice to another
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}
slice1 = append(slice1, slice2...)Maps are Go's built-in associative data type (sometimes called dictionaries or hash tables in other languages):
// Declaration
var m map[string]int
// Initialization
m = make(map[string]int)
// Declaration and initialization
m := map[string]int{
"apple": 5,
"banana": 10,
"orange": 8,
}
// Access
count := m["apple"]
// Check if key exists
count, exists := m["grape"]
if !exists {
fmt.Println("Grape not found")
}
// Delete
delete(m, "apple")
// Iteration
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}Structs are collections of fields:
// Define a struct
type Person struct {
Name string
Age int
Address string
}
// Create an instance
var p Person
p.Name = "John"
p.Age = 30
p.Address = "123 Main St"
// Create with initialization
p := Person{
Name: "John",
Age: 30,
Address: "123 Main St",
}
// Shorthand (order matters)
p := Person{"John", 30, "123 Main St"}type Address struct {
Street string
City string
ZipCode string
}
type Person struct {
Name string
Age int
Address Address
}
p := Person{
Name: "John",
Age: 30,
Address: Address{
Street: "123 Main St",
City: "New York",
ZipCode: "10001",
},
}Methods are functions with a special receiver argument:
type Rectangle struct {
Width float64
Height float64
}
// Method with value receiver
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Method with pointer receiver (can modify the receiver)
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// Usage
rect := Rectangle{Width: 10, Height: 5}
area := rect.Area()
rect.Scale(2)Interfaces define a set of method signatures:
type Shape interface {
Area() float64
Perimeter() float64
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// Function using interface
func PrintShapeInfo(s Shape) {
fmt.Printf("Area: %f, Perimeter: %f\n", s.Area(), s.Perimeter())
}
// Usage
r := Rectangle{Width: 10, Height: 5}
c := Circle{Radius: 7}
PrintShapeInfo(r)
PrintShapeInfo(c)Go handles errors using the built-in error type:
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Usage
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}type DivisionError struct {
Dividend float64
Divisor float64
}
func (e *DivisionError) Error() string {
return fmt.Sprintf("cannot divide %f by %f", e.Dividend, e.Divisor)
}
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, &DivisionError{a, b}
}
return a / b, nil
}Goroutines are lightweight threads managed by the Go runtime:
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world") // New goroutine
say("hello") // Current goroutine
}Channels are used for communication between goroutines:
// Unbuffered channel
ch := make(chan int)
// Sending on a channel
go func() {
ch <- 42 // Send value to channel
}()
// Receiving from a channel
value := <-ch // Receive value from channel
// Buffered channel
ch := make(chan int, 2) // Can hold up to 2 values
// Closing a channel
close(ch)
// Ranging over a channel
for value := range ch {
fmt.Println(value)
}The select statement lets a goroutine wait on multiple channel operations:
select {
case msg1 := <-ch1:
fmt.Println("Received from ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("Received from ch2:", msg2)
case ch3 <- msg3:
fmt.Println("Sent to ch3:", msg3)
case <-time.After(1 * time.Second):
fmt.Println("Timeout")
default:
fmt.Println("No communication")
}WaitGroup is used to wait for a collection of goroutines to finish:
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Decrements the counter when the goroutine completes
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1) // Increment the WaitGroup counter
go worker(i, &wg)
}
wg.Wait() // Block until the WaitGroup counter is zero
fmt.Println("All workers done")
}Go has a built-in testing framework. Create files ending with _test.go:
// math.go
package math
func Add(a, b int) int {
return a + b
}// math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}Run tests with:
go test- Use gofmt: Always format your code with
gofmtorgo fmt. - Error handling: Always check for errors and handle them appropriately.
- Comments: Use comments to document your code. Functions exported from a package should have comments.
- Naming conventions:
- Package names: lowercase, single-word, no underscores.
- Function/variable names: camelCase for unexported, PascalCase for exported.
- Avoid global variables: They make testing harder and create hidden dependencies.
- Keep interfaces small: Interfaces with fewer methods are more reusable.
- Use defer for cleanup: Use
deferfor closing files, unlocking mutexes, etc.
