Packages & Import packages#
- package is a namespace that groups related Go files together.
- by convention, package name is the last directory name where the sources are stored eg-
import "math/rand" , rand is the directory name where the sources are stored and will have package rand for all sources. - directory which stores all related go source files
/*
mathutils/
add.go
subtract.go
*/
package mathutils // Both uses mathutils lib
- Single Import package, multiple packages import, alias imports & blank imports
import "fmt" // Single Import
// Multiple Imports
import (
"fmt"
"math"
)
// Alias Imports
import m "math"
import (
f "fmt"
m "math"
)
// Blank Imports => forces to run init() of the package, but don't want to use its symbols.
import _ "net/http/pprof"
- Special
package main -> Entry point to the application. And must have main() method as the entry point to the application. main() function takes no arguments and returns nothing.- Package can have automatic initialisation function, which runs before main()
func init() {
fmt.Println("Package initialized")
}
// Order of execution
/*
imported packages init()
↓
current package init()
↓
main()
*/
- Package visibility is handled with identifiers which starts with capital case i.e
- Name -> Visible outside the package
- name -> Only visible inside the package
Modules#
- Creating new Module
go mod init github.com/user/project - Importing module
import "github.com/user/project/mathutils"
Functions#
- A function in Go is a reusable block of code that performs a specific task.
- Parameter vs Arguments
| Term | Meaning |
|---|
| Parameters | Variables defined in function signature |
| Arguments | Actual values passed to the function |
// Decleration practices
func functionName(parameters) returnType {
// function body
}
// Decleration & calling
func add(a int, b int) int { // parameters
return a + b
}
result := add(3, 5) // arguments
- Types Grouping & Multiple returns
func add(a, b int) int {
return a + b
}
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
q, err := divide(10, 3) // Multiple Return, Mainly used in error handling
- Named return in functions.
func rectangle(l, w int) (area int) {
area = l * w
return
}
- Variadic functions accept variable number of arguments.
func sum(nums ...int) int {
total := 0
for _, v := range nums {
total += v
}
return total
}
// Usage
sum(1,2,3)
sum(10,20,30,40)
nums := []int{1,2,3}
sum(nums...)
- Functions are first-class citizens (can be assigned to variables & passed).
- Annonymouns functions & lambdas
go func() {
fmt.Println("hello")
}()
- closure is a function that captures variables from its surrounding scope.
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// Usage
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
func modify(x int) {
x = 10
}
a := 5
modify(a)
fmt.Println(a) // still 5
- Pass by reference (still pass by value, but the value which is passed is a pointer to the original varaible)
func modify(x *int) {
*x = 10
}
a := 5
modify(&a)
fmt.Println(a) // 10
- Struct methods -
r Rectangle is called a receiver (value receiver). & pointer receiver (helps avoid copying a large struct)
type Rectangle struct {
width int
height int
}
func (r Rectangle) Area() int { // value reciever
return r.width * r.height
}
func (r *Rectangle) Scale(factor int) { // pointer reciever
r.width *= factor
}
- defer function call is heavily used for locks, safe file closing etc. This uses LIFO ordering of stack. executed when surrounding method is completed.
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
/*
3
2
1
*/
Datatypes & Variables#
var age int = 25 // Type explicity specified
age := 25 // Type implicity inferred (short hand decleration), ONLY functions scope (NOT in global scope).
- Type implicitly inferred, can only be used within the functions. It cannot be used in global scope.
- Primitive types
// Whole Numbers
int
int8
int16
int32
int64
uint
uint8
uint16
uint32
uint64
// Floating Numbers
float32
float64
//Boolean true/false
bool
// String - immutable sequence of bytes
string
/*
- immutable
- UTF-8 encoded
- concatenation using `+`
*/
// Arrays => Fixed size, cannot be changed once its already declated & initialized.
var nums [3]int = [3]int{1,2,3}
// Slices => Dynamic size, automatically resized
nums := []int{1,2,3}
/*
- pointer to array
- length
- capacity
*/
// Maps => key-value pairs
ages := map[string]int{
"John": 25,
"Anna": 30,
}
value := ages["John"] // fetching values
age, ok := ages["John"] // checking if value exists
// Struct
type User struct {
Name string
Age int
}
u := User{
Name: "John",
Age: 25
}
| Type | Default value |
|---|
| int | 0 |
| float | 0.0 |
| bool | false |
| string | "" |
| slice | nil |
| map | nil |
| pointer | nil |
| struct | zero values for all fields |
var age int
fmt.Println(age) // 0
- Constants
- Constants MUST be known at compile time. Otherwise the compiler will panic.
- Generally numbers, strings, booleans or expressions made from those are good candidates for constants.
- Hence, maps, slices, struct and arrays CANNOT be constants. these are runtime data structures & involves memory allocation at runtime.
const pi float64 = 3.14159
- Multiple declerations, variable shadowing,
var a, b int = 1, 2
var (
name string = "John"
age int = 30
)
// Shadowing
x := 10
if true {
x := 20
fmt.Println(x) // 20
}
fmt.Println(x) // 10
- nil values
- pointers
- slices
- maps
- channels
- interfaces
- functions
- Iota - auto increment values (Similar to enum)
const (
Sunday = iota
Monday
Tuesday
)
Sunday // value 0
type Age int
var myAge Age = 25
- Blank Identifier for return values
value, _ := someFunction() - Type conversion / Type casting
var a int = 10
var b float64 = float64(a)
- Type conversions between string & primitive types
import "strconv"
var intString string = "42"
var i, err = strconv.Atoi(intString)
var number int = 12
var s string = strconv.Itoa(number)
- Comments should explain why, NOT just what
- Single-Line Comments - Used for short explanations.
// This is a single-line comment
x := 10
- Multi-Line Comments - Used for longer explanations or temporarily disabling code.
/*
This is a
multi-line comment
*/
- Documentation Comments (Important) - Go uses comments to generate documentation automatically.
- Documentation comments:
- start with the name of the thing being described
- end with a period
// Add returns the sum of two integers.
func Add(a, b int) int {
return a + b
}
- Package Comments - Every package should have a package-level comment.
- Placed before the package declaration.
// Package mathutils provides basic math helper functions.
package mathutils
“fmt” package#
- Mainly used for
- formatting strings
- reading user input
fmt.Print("Hello")
fmt.Print("World")
// HelloWorld
fmt.Println("Hello")
fmt.Println("World")
/*
Hello
World
*/
fmt.Printf("Age: %d", 25)
// Age: 25
| Verb | Meaning |
|---|
| %v | Default value representation |
| %+v | Struct with field names |
| %#v | Go syntax representation |
| %T | Type of value |
| %d | Integer |
| %f | Floating-point number |
| %s | String |
| %t | Boolean |
| %p | Pointer address |
- formatted string instead of printing.
msg := fmt.Sprintf("User: %s Age: %d", "John", 25)
err := fmt.Errorf("invalid user id: %d", id)
- Scan - Reads input from console.
- Scanln - Reads input until newline.
- Scanf - Reads formatted input.
// scan with default space delimiter
var name string
fmt.Scan(&name)
// scan with \n as delimiter
fmt.Scanln(&name)
// scan specific type
var age int
fmt.Scanf("%d", &age)