Hone logo
Hone
Problems

Mastering Defer in Go: Resource Cleanup and Execution Order

Defer statements in Go are a powerful tool for ensuring resource cleanup and executing code at the end of a function, regardless of how the function exits (normal completion, panic, or return). This challenge will test your understanding of defer's behavior, particularly its execution order and how it's used for resource management.

Problem Description

You are tasked with creating a Go program that demonstrates the proper usage of defer for resource cleanup. Specifically, you need to implement a function processFile(filename string) error that:

  1. Opens a file with the given filename.
  2. Reads and processes the file's contents (for this challenge, simply print the contents to the console).
  3. Crucially, uses defer to ensure the file is closed, regardless of whether the file processing succeeds or encounters an error.
  4. Returns an error if the file cannot be opened.

The challenge focuses on demonstrating that the defer statement executes after the function returns, even if a return statement is encountered within the function due to an error. The order of deferred calls should also be observed.

Examples

Example 1:

Input: "test.txt" (where test.txt contains "Hello, world!")
Output:
Hello, world!

Explanation: The file "test.txt" is opened, its contents are printed, and then the file is closed via the deferred function.

Example 2:

Input: "nonexistent.txt"
Output:
Error opening file: open nonexistent.txt: no such file or directory

Explanation: The file "nonexistent.txt" cannot be opened, resulting in an error. The function returns early, but the deferred file close still executes.

Example 3: (Demonstrating deferred call order)

package main

import (
	"fmt"
	"os"
)

func processFile(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}

	defer fmt.Println("Deferred: File closed") // Deferred call 1
	defer file.Close()                      // Deferred call 2

	buf := make([]byte, 100)
	n, err := file.Read(buf)
	if err != nil {
		return err
	}
	fmt.Println(string(buf[:n]))

	return nil
}

func main() {
	err := processFile("test.txt")
	if err != nil {
		fmt.Println("Error:", err)
	}
}

If test.txt contains "Hello, world!", the output will be:

Hello, world!
Deferred: File closed

Explanation: The file is opened, contents are read and printed. The deferred calls execute in LIFO (Last-In, First-Out) order. file.Close() is executed first, then fmt.Println("Deferred: File closed").

Constraints

  • The program must compile and run without errors.
  • The processFile function must handle the case where the file does not exist.
  • The file must be closed using a defer statement.
  • The deferred file close must execute regardless of whether the file processing succeeds or fails.
  • The deferred calls should execute in LIFO order.
  • Assume the file contents are always ASCII text.

Notes

  • Consider using os.Open to open the file.
  • Remember that defer schedules a function call to be executed immediately before the surrounding function returns.
  • Think about the order in which deferred functions are executed when multiple defer statements are present. They execute in LIFO order.
  • Error handling is important. Ensure your code gracefully handles potential errors during file operations.
  • The focus is on demonstrating the correct usage of defer for resource cleanup, not on complex file processing logic.
Loading editor...
go