Defer Statements
Defer statements in Harneet provide a powerful mechanism for ensuring cleanup code runs when a function exits, regardless of how it exits (normal return, early return, or error). This is particularly useful for resource management, cleanup operations, and ensuring consistent program behavior.
Basic Syntax
| Basic Defer Syntax |
|---|
| defer functionCall()
defer fmt.Println("This runs when function exits")
|
How Defer Works
- Execution Order: Deferred calls execute in LIFO (Last In, First Out) order
- Timing: Deferred calls execute just before the function returns
- Argument Capture: Arguments are evaluated and captured when the defer statement is encountered
- Guaranteed Execution: Deferred calls execute regardless of how the function exits
Basic Example
| Basic Example |
|---|
| package main
import fmt
function basicDeferExample() {
fmt.Println("Function start")
defer fmt.Println("This will execute last")
defer fmt.Println("This will execute second to last")
fmt.Println("Function middle")
fmt.Println("Function end")
}
function main() {
basicDeferExample()
}
|
Output:
| Function start
Function middle
Function end
This will execute second to last
This will execute last
|
Resource Management
Defer is commonly used for cleanup and resource management:
| Resource Management |
|---|
| package main
import fmt
function resourceManagementExample() {
fmt.Println("Opening resource...")
defer fmt.Println("Closing resource (cleanup)")
defer fmt.Println("Releasing memory")
defer fmt.Println("Logging operation complete")
fmt.Println("Using resource...")
fmt.Println("Processing data...")
// Resource is automatically cleaned up via defer
}
|
Defer with Multiple Return Paths
Defer ensures cleanup happens regardless of which return path is taken:
| Multiple Return Paths |
|---|
| package main
import fmt
function multipleReturnPaths(condition bool) {
defer fmt.Println("Cleanup always happens")
if condition {
fmt.Println("Taking early return path")
return // Defer still executes
}
fmt.Println("Taking normal execution path")
// Defer executes here too
}
|
Argument Capture
Arguments to deferred functions are captured when the defer statement is encountered:
| Captured Variables |
|---|
| package main
import fmt
function deferWithCapturedVariables() {
var message = "Initial message"
// This defer captures the current value of message
defer fmt.Printf("Deferred message: %s\n", message)
// Change the message after defer
message = "Modified message"
fmt.Printf("Current message: %s\n", message)
// The deferred call will still use "Initial message"
}
|
Defer with Anonymous Functions
You can defer user-defined functions for more complex cleanup logic:
| User-Defined Functions |
|---|
| package main
import fmt
function cleanupFunction() {
fmt.Println("Performing cleanup operations")
}
function deferWithUserFunctions() {
var counter = 0
defer cleanupFunction()
defer fmt.Printf("Final counter value: %d\n", counter)
counter = 5
fmt.Printf("Counter set to: %d\n", counter)
}
|
Defer in Loops
Each iteration of a loop creates a separate deferred call:
| Defer in Loops |
|---|
| package main
import fmt
function deferInLoop() {
fmt.Println("Starting loop with defer")
for var i = 0; i < 3; i = i + 1 {
defer fmt.Printf("Deferred from iteration %d\n", i)
fmt.Printf("Loop iteration %d\n", i)
}
fmt.Println("Loop completed")
}
|
Output:
| Starting loop with defer
Loop iteration 0
Loop iteration 1
Loop iteration 2
Loop completed
Deferred from iteration 2
Deferred from iteration 1
Deferred from iteration 0
|
Error Handling with Defer
Defer statements execute even when errors occur:
| Defer with Errors |
|---|
| package main
import fmt
function functionWithError() {
defer fmt.Println("Cleanup happens even with errors")
fmt.Println("About to cause an error...")
var result = 10 / 0 // This will cause an error
fmt.Printf("Result: %d\n", result) // This won't execute
// But defer still executes
}
|
Comprehensive Error Handling
Harneet's defer implementation includes advanced error handling:
- Error Aggregation: Multiple defer errors are collected and reported together
- Stack Trace Preservation: Error stack traces are maintained for debugging
- Panic Recovery: Panics in deferred calls are caught and converted to errors
- Resource Limits: Protection against excessive defer stack usage
| Error Function |
|---|
| package main
import fmt
function errorFunction() {
var x = 1 / 0 // Error in defer
}
function multipleDeferredErrors() {
defer fmt.Println("This defer works")
defer errorFunction() // This will fail
defer fmt.Println("This defer also works")
fmt.Println("Main function completes")
// All working defers execute, errors are aggregated
}
|
Best Practices
1. Use Defer for Cleanup
| Defer for Cleanup |
|---|
| function processFile(filename string) {
// Open file
defer closeFile() // Ensure file is closed
// Process file...
}
|
| Defer After Acquisition |
|---|
| function acquireResource() {
var resource = openResource()
defer resource.close() // Defer immediately after acquisition
// Use resource...
}
|
3. Be Careful with Loop Variables
| Loop Variables |
|---|
| // Correct: Capture loop variable
package main
import fmt
for var i = 0; i < 5; i = i + 1 {
defer fmt.Printf("Iteration %d\n", i) // Captures current value of i
}
// Alternative: Use a helper function if you need final value
var finalI = 0
function printFinalI() {
fmt.Printf("Final i: %d\n", finalI)
}
for var i = 0; i < 5; i = i + 1 {
finalI = i
defer printFinalI() // Uses final value of i
}
|
4. Order Matters
| Defer Order |
|---|
| function setupAndCleanup() {
setupA()
defer cleanupA() // Executes second
setupB()
defer cleanupB() // Executes first
// Use resources...
}
|
Advanced Features
Stack Overflow Protection
Harneet includes protection against excessive defer usage: - Maximum defer stack size limits - Recursive defer call detection - Emergency cleanup procedures
Error Aggregation
When multiple deferred calls fail, Harneet aggregates the errors: - Detailed error reporting for each failed defer - Stack trace preservation - Continued execution of remaining defers
- Defer has minimal runtime overhead
- Arguments are captured efficiently
- Stack management is optimized for typical usage patterns
Common Patterns
File Operations
| File Operations |
|---|
| function processFile(filename string) {
var file = openFile(filename)
defer file.close()
// Process file safely
}
|
Lock Management
| Lock Management |
|---|
| function criticalSection() {
mutex.lock()
defer mutex.unlock()
// Critical section code
}
|
Logging and Monitoring
| Logging and Monitoring |
|---|
| package main
import fmt
function trackedOperation(name string) {
fmt.Printf("Starting %s\n", name)
defer fmt.Printf("Completed %s\n", name)
// Operation code
}
|
Error Recovery
| Error Recovery |
|---|
| package main
import fmt
function cleanupOperation() {
fmt.Println("Operation cleanup")
}
function safeOperation() {
defer cleanupOperation()
// Potentially risky operation
}
|
Summary
Defer statements provide a robust mechanism for:
- Resource Management: Automatic cleanup of resources
- Error Safety: Guaranteed execution even with errors
- Code Organization: Clean separation of setup and teardown
- Reliability: Consistent behavior across all exit paths
The defer mechanism in Harneet is designed to be both powerful and safe, with comprehensive error handling and protection against common pitfalls.