Map
- A map is a built-in data structure used to store key-value pairs.
- Each key is unique and maps to a value.
- Maps are unordered - iteration order is not guaranteed
- Maps are reference types - passed by reference to functions
- Not safe for concurrent use - use sync.Map or mutex for concurrent access
// Decleration
var m map[string]int // A declared map is `nil` until initialized.
/*
- `string` → key type
- `int` → value type
*/
// Initialization
m := make(map[string]int) // using make
m := map[string]int{ // using map literal
"apple": 10,
"banana": 20,
}
// Adding & Updating values
m["apple"] = 30
m["orange"] = 15
// If the key exists → value is updated.
// If the key does not exist → a new entry is created.
// Accessing values
value := m["apple"]
value, exists := m["apple"] // if value doesn't exist value=zero value & exists=false
// Deleting from map
delete(m, "apple")
// Iterate over map
for key, value := range m {
fmt.Println(key, value)
}
// Length of map - count of key value pairs exising
len(m)Pointers
- A pointer is a variable that stores the memory address of another variable.
- Pointers allow functions to modify the original value instead of working on a copy.
- Go does not support pointer arithmetic.
- A pointer stores a memory address.
&→ address operator.*→ dereference operator.- slices & maps are already pointers
// Decleration
var p *int
// `p` → pointer variable
// `*int` → pointer to an integer
x := 10
p := &x
// `x` → value
// `&x` → memory address of `x`
// `p` now stores the address of `x`
// Dereferencing a pointer
x := 10
p := &x
fmt.Println(*p) // 10
// Modifying value using pointer
x := 10
p := &x
*p = 20 // x = 20 now
// Passing pointer to a function (pass by value, the catch is value is a pointer to a variable.)
func update(n *int) {
*n = 50
}
x := 10
update(&x) // x updated to 50
// Creating a pointer using new
p := new(int) // Default value is 0 for int
// Pointer to a struct (go automatically dereferences struct pointer)
type Person struct {
name string
age int
}
p := &Person{name: "John", age: 25}
// Access using p.name & p.age, no need for (*p).nameMethod
- Functions with special reciever
- A method is a function associated with a specific type.
- It allows you to define behavior for a type.
func (receiver Type) MethodName(parameters) returnType {
// method body
}
type Rectangle struct {
width int
height int
}
func (r Rectangle) Area() int {
return r.width * r.height
}
rect := Rectangle{width: 10, height: 5}
area := rect.Area()- Value & Pointer receiver
- When to use pointer receiver:
- Need to modify the receiver
- Receiver is a large struct (avoid copying)
- Consistency (if some methods use pointer receivers, all should)
func (r Rectangle) Area() int { // Value reciever, The method receives a **copy of the value**.
return r.width * r.height
}
func (r *Rectangle) Scale(factor int) { // Pointer reciever, recieves a pointer to the type
r.width = r.width * factor
r.height = r.height * factor
}Interfaces
- An interface defines a set of method signatures.
- Any type that implements those methods automatically satisfies the interface.
- Go uses implicit implementation (no
implementskeyword). - interfaces often end with
er, like Reader, Writer, Stringer etc. - Since Go 1.18,
anyis an alias forinterface{} - An interface value contains two things:
- Concrete value
- Type of the value
// Declaring a interface
type Shape interface {
Area() float64
}
type Shape2 interface {
Area() float64
Perimeter() float64
}
// Any type that defines `Area() float64` satisfies `Shape`.
// Implementing a interface
type Rectangle struct {
width float64
height float64
}
func (r Rectangle) Area() float64 {
return r.width * r.height
}
// Using an Interface
func PrintArea(s Shape) {
fmt.Println(s.Area())
}
rect := Rectangle{10, 5}
PrintArea(rect)- Empty Interface - The empty interface has no methods. Can hold any value
interface{}
var v interface{}
v = 10
v = "hello"- Type assertions - Used to extract the underlying value from an interface. if assertions fails then they cause panic(full termination of program)
// Type assertions
var v interface{}
v = 10
v := i.(int)
v, ok := i.(int)
// Type Switches, Switch for type assertions
switch v := i.(type) {
case int:
fmt.Println("int", v)
case string:
fmt.Println("string", v)
default:
fmt.Println("unknown type")
}Error Interface
- Error interface
type error interface {
Error() string
}
// usage
func DoSomething() (int, error) {
// ...
}
// create a new error object
var ErrNotFound = errors.New("resource was not found")
// nil for no error
// Create custom error
type MyCustomError struct {
message string
details string
}
func (e *MyCustomError) Error() string {
return fmt.Sprintf("%s, details: %s", e.message, e.details)
}
func someFunction() error {
// ...
return &MyCustomError{
message: "...",
details: "...",
}
}
”stringer” interface
- Stringer is an interface for defining the string format of values. The interface consists of a single
Stringmethod:
type Stringer interface {
String() string
}Types that want to implement this interface must have a String() method that returns a human-friendly string representation of the type.
- similar to overriding the
toString()method in Java.