Skip to Content
Go Realm v1 is released 🎉
TopicsDefer

Named Return Values

  • Go’s return values may be named
  • These variables are automatically initialized to their zero values and can be used throughout the function.
  • A return statement without arguments returns the named return values
  • Named returns are not the same as function parameters
  • Can still explicitly return values: return x, y
  • All named values must be returned

Basic Syntax

func calculate(x, y int) (sum int, product int) { sum = x + y product = x * y // implicitly returns sum and product return // naked return }

Key Features

  1. Auto-initialization - Variables are initialized to their zero values
  2. Naked returns - Can use simple return statement without specifying values
  3. Documentation - Makes return values self-documenting

When to Use

Multiple return values - Improves readability
Deferred operations - Useful with defer statements
Long functions - Makes return values clearer

When to Avoid

Short, simple functions - May be overkill
When return values change often - Can cause confusion

In Go (Golang), The defer keyword delays the execution of a function until the surrounding function completes and after the return statement is processed.

defer:

  • The defer keyword delays the execution of a function until the surrounding function completes.
  • Used for cleanup tasks (closing files, unlocking mutexes, etc.).
  • Execution Order: Last-in, first-out (LIFO).
  • They execute after the return statement is processed, but before the function actually exits.
  • Arguments are evaluated immediately, not when the deferred function is executed.

📌 Basic Example:

package main import "fmt" func main() { fmt.Println("Start") defer fmt.Println("Deferred 1") defer fmt.Println("Deferred 2") fmt.Println("End") }

🧠 Output:

Start End Deferred 2 Deferred 1

⚠️ Important Note: Arguments are evaluated immediately

func main() { x := 10 defer fmt.Println("Deferred with x =", x) x = 20 }

Output:

Deferred with x = 10

Even though x becomes 20 later, defer uses its value at the time of deferral.


Example 1: Multiple Defers (LIFO Order)

package main import "fmt" func main() { defer fmt.Println("First defer") defer fmt.Println("Second defer") defer fmt.Println("Third defer") fmt.Println("Main function") }

Output:

Main function Third defer Second defer First defer

🧠 Why? Because defer statements are executed in reverse order of how they were deferred.


Example 2: Defer in a Loop

package main import "fmt" func main() { for i := 1; i <= 3; i++ { defer fmt.Println("Deferred", i) } }

Output:

Deferred 3 Deferred 2 Deferred 1

🧠 Why? Even though i changes, the defer fmt.Println("Deferred", i) captures the current value at runtime, not at execution — because it’s not wrapped in a function. But watch this next case…


🔁 Example 3: Named Return vs Unnamed Return

The difference in output between namedReturn() and unnamedReturn() is due to how Go handles named return values versus unnamed return values with defer. Let’s break it down:

1. Named Return (namedReturn())

func namedReturn() (result int) { defer func() { result = 99 }() return 10 // Returns 99 }
  • result is a named return variable.

  • When you do return 10, it assigns 10 to result, and then:

    1. All defer functions run after the return value is set, but before the function actually returns.
    2. So the deferred function overwrites result to 99.
  • Final return value: 99

Key Insight

  • Named return values are like “mutable buckets” that can be modified by defer.
  • The return statement stores the value in result, but defer can overwrite it.

2. Unnamed Return (unnamedReturn())

func unnamedReturn() int { result := 10 defer func() { result = 99 }() return result // Returns 10 }
  • No named return value; result is a local variable.
  • return result copies the current value of result (10) to the function’s return slot.
  • The defer runs and modifies result to 99, but the return slot already holds 10.
  • The function returns 10.

Key Insight

  • Unnamed returns copy the value at the return statement.
  • defer can’t modify the return value after the copy.

Why the Difference?

BehaviorNamed Return (result int)Unnamed Return (int)
Return value storageShared variable (result)Anonymous slot
return X semanticsresult = XCopy X to return slot
defer can modify✅ Yes❌ No

Visualization

Named Return (namedReturn())

func namedReturn() (result int) { result = 10 // Step 1: Assign 10 to result defer runs: result = 99 // Step 2: Overwrite result return // Step 3: Return result (now 99) }

Unnamed Return (unnamedReturn())

func unnamedReturn() int { result := 10 // Local variable = 10 defer: result = 99 // Scheduled to run LATER return result // 1. Evaluate result (10) → store in return slot // 2. Run defer (result = 99) → too late! // 3. Return 10 }

Practical Implications

  • Named returns are useful when you need defer to adjust the return value (e.g., error handling).
  • Unnamed returns are safer if you want defer to run but not affect the return value.
// Named return with defer (common in error handling) func readFile() (content string, err error) { file, err := os.Open("foo.txt") if err != nil { return "", err // Early return } defer file.Close() // Ensures cleanup // ... read file into content return content, nil // defer can't modify content }

🧠 Key Takeaway

Named returns allow defer to modify the return value.
Unnamed returns lock in the value at the return statement.

This behavior is specific to Go and is crucial for understanding control flow with defer.


Example 4: Defer and Closures

package main import "fmt" func main() { for i := 1; i <= 3; i++ { defer func() { fmt.Println("Deferred inside closure:", i) }() } }

Output:

Deferred inside closure: 4 Deferred inside closure: 4 Deferred inside closure: 4

🧠 Why?

  • The deferred function is a closure that captures i by reference.
  • After the loop ends, i becomes 4, and then the closures run — all printing 4.
  • To fix this, pass i as a parameter to the function:
for i := 1; i <= 3; i++ { defer func(n int) { fmt.Println("Deferred with value:", n) }(i) }

✅ Output:

Deferred with value: 3 Deferred with value: 2 Deferred with value: 1

Key Behaviors

(A) Arguments Evaluated Immediately

func main() { x := "Hello" defer fmt.Println(x) // "Hello" (evaluated now) x = "World" }

Output:
Hello (not World)

(B) Works with Named Return Values

func count() (result int) { defer func() { result++ }() return 5 // Returns 6 }

Output:
6 (deferred function modifies result)

🧠 Guess the Output

// Named Return func calculate() (result int){ fmt.Println("first", result) defer func() { result = result + 10 fmt.Println("defer", result) }() result = 5 fmt.Println("second", result) return } func main() { namedReturn := calculate() fmt.Println("Named Return", namedReturn) }

Hint: If parent function exists, both parent & closure function shares same *variable (pointer reference). Output:

first 0 second 5 defer 15 Named Return 15
// Unnamed Return func calculate() int{ result := 0 fmt.Println("first", result) defer func() { result = result + 10 fmt.Println("defer", result) }() result = 5 fmt.Println("second", result) return result } func main() { unnamedReturn := calculate() fmt.Println("Unnamed Return", unnamedReturn) }

Output:

first 0 second 5 defer 15 Unnamed Return 5 (why?)

Named Return Values

  1. Code Execution: All normal code in the function executes.
  2. Defer Registration: Defer functions are stored in a LIFO stack (the “magic box”).
  3. Return Handling: The return statement triggers execution of all deferred functions (in reverse order of registration).
  4. Final Return: The function returns the current values of the named return variables.

Anonymous Return Values

  1. Code Execution: All normal code in the function executes.
  2. Defer Registration: Defer functions are stored in a LIFO stack.
  3. Return Evaluation: ⭐Return values are evaluated and stored in a temporary variable⭐ at the return statement.
  4. Defer Execution: All deferred functions execute (but cannot modify the already-stored return values).
  5. Final Return: The function returns the pre-evaluated values from step 3.

Key Difference

Named return Deferred functions can modify the return values.
Unnamed return Return values are “locked in” when the return statement is evaluated, before defer execution.