06a Functions in Go (Part 1)
Main topics:
- Defining and Calling Functions
- Function Parameters
- Function Return Values
- Scope of Variables
- Recursion
- Variable Shadowing
- Function Signatures
Functions in Go work the same way as in other languages like Python, Java, C++, and JavaScript; They’re just a way to group code which we want to use many times.
The syntax might look different, but the basic idea is the same.
1. Defining and calling functions
The if statement in Go is used to execute code conditionally—only when a given boolean expression is true.
2. Function parameters
The if statement in Go is used to execute code conditionally—only when a given boolean expression is true.
Some exampls are shown below:
3. Function return values
In Go, a function’s return type specifies the type of value it sends back to the caller after execution.
Some exampls are shown below:
Multiple return values
Some examples
"error" package in Go
The errors package provides simple error handling primitives in Go.
It’s part of the standard library and mainly helps you create and manipulate error values, which are used to indicate when something has gone wrong during program execution.
-
Creating errors: The most common way to create a new error is using errors.New(), which takes a string message describing the error.
-
Error type: In Go, error is an interface type with a single method:
Any type that implements this method is an error. -
Error handling pattern: Functions often return an error as the last return value, which you check to see if something went wrong.
package main
import (
"errors"
"fmt"
)
// Function that returns an error if input is negative
func checkPositive(number int) error {
if number < 0 {
return errors.New("input cannot be negative")
}
return nil // no error
}
func main() {
nums := []int{10, -5, 0}
for _, num := range nums {
err := checkPositive(num)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println(num, "is positive or zero")
}
}
}
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
var err error // explicitly declare an error variable
var result float64
if b == 0 {
err = errors.New("cannot divide by zero")
return 0, err
}
result = a / b
return result, nil
}
func main() {
var err error // explicitly declare error variable to hold function return
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
result, err = divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
Named return values
Named return values are like pre-declared variables for the return values of a function. Instead of just specifying the types of the return values, you also give each return value a name in the function signature.
Example:
func divide(a, b float64) (result float64, err error) {
if b == 0 {
err = fmt.Errorf("cannot divide by zero")
return // returns result=0 (default float64), and err
}
result = a / b
return // returns the values of result and err
}
Blank identifier
The blank identifier _ is a special placeholder used when you want to ignore a value. It tells the Go compiler that you don’t care about that particular value, and it prevents compilation errors about unused variables.
4. Pass by value or pass by pointer
-
Go always passes function arguments by value. This means the function gets a copy of the argument.
-
If you want the function to modify the original variable, you need to pass a pointer to that variable (same concept as pointer parameters or reference parameter in C/C++)
The function receives a copy of the argument. Changes inside the function don’t affect the original variable.
We pass the address of the variable (*int), so the function can modify the original value.
5. Scope of variables
In Go, scope refers to where a variable can be accessed within your code.
-
A local variable is declared inside a function and can only be used within that function.
-
A global variable is declared outside of any function (usually at the top of the file) and is accessible from any function in the same package.
-
Rules are very similar to C/C++
Note: Prefer local variables when possible to avoid unwanted side effects and make your code easier to debug.
6. Recursion in Go
Recursion is a programming technique where a function calls itself to solve smaller instances of a problem. It continues until it reaches a base case that stops further calls.
Examples:
7. Variable shadowing
Variable shadowing occurs when a new variable declared in a nested (inner) scope has the same name as a variable in an outer scope. The inner variable "shadows" or hides the outer one within its scope.
This can lead to confusion if not handled carefully.
Shadowing if or for block
Example if or for block
package main
import "fmt"
func main() {
x := 5
fmt.Println("Before if:", x)
if x := 100; x > 0 { // New x shadows outer x
fmt.Println("Inside if:", x)
}
fmt.Println("After if:", x) // Still refers to the original x
}
Output
8. Function types and signature
This is same concept as function prototype C/C++ or "function signature" in Java.
A function signature defines:
- The parameter types
- The return type(s)
- It tells you what a function expects and returns — like a contract.
This improves code readability, especially when passing functions around.
Example