Skip to Content
Go Realm v1 is released 🎉
TopicsVariadic Function

Variadic Functions in Go

1. What is a variadic function?

A function that accepts any number of arguments of the same type.
Syntax:

func sum(numbers ...int) int { // numbers becomes []int inside function total := 0 for _, num := range numbers { total += num } return total }

2. How does Go implement variadic functions?

Go converts arguments into a slice.

  • foo(1, 2, 3)values := []int{1, 2, 3}
  • If no args: values is an empty slice (not nil).

3. Can we pass a slice to a variadic function?

Yes, with slice... syntax:

nums := []int{1, 2, 3} foo(nums...) // Equivalent to foo(1, 2, 3)

4. Can a variadic function handle multiple types?

Not directly, but use interface{} (loses type safety):

func handle(vals ...interface{}) { for _, v := range vals { switch v.(type) { case int: // handle int case string: // handle string // ... } } }

5. Can we have multiple variadic parameters?

No. Rules:

  1. Only one variadic parameter per function.
  2. Must be the last parameter.

Example:

func greet(prefix string, names ...string) { } // Valid func fail(a ...int, b ...int) { } // Compile error

6. What if no arguments are passed?

The parameter becomes an empty slice ([]T{}), not nil. Always check length:

func safeSum(nums ...int) int { if len(nums) == 0 { return 0 // Handle empty case } // ... sum logic ... }

7. Key advantages of variadic functions?

  1. Avoids writing overloaded functions (e.g., Sum1(int), Sum2(int, int)).
  2. Cleaner APIs (e.g., fmt.Println()).
  3. Flexible for helper functions (e.g., joining strings).

8. Key limitations?

  1. Single type only (without interface{} hacks).
  2. Slice allocation overhead for each call.
  3. No compile-time checks for minimum arguments.

9. Practical example: String joiner

func Join(sep string, strs ...string) string { return strings.Join(strs, sep) } // Usage: Join(", ", "A", "B", "C") // "A, B, C"

10. Anti-patterns to avoid

  1. Unchecked interface{}:
    func risky(vals ...interface{}) { n := vals[0].(int) // Panics if not int! }
  2. Hidden allocations (variadic calls create new slices).
  3. Ignoring empty args: Always handle len(values) == 0.

Memory Tricks

  • ... = “unpack” (when passing slices) or “variable” (in declaration).
  • Slice inside: Think of ...T as []T in the function body.
  • Last parameter only: Like a “greedy” argument collector.

Advanced Patterns

Mixed Parameter Types

func process(prefix string, values ...interface{}) { // Can handle multiple types via empty interface // Requires type assertion for actual usage }

Forwarding Arguments

func log(values ...interface{}) { fmt.Println(values...) }

Pro Tips for Interviews

  1. Memory Efficiency:

    • Each variadic call creates a new slice
    • For performance-critical code, consider passing a pre-allocated slice
  2. Error Handling:

    • Variadic functions can’t enforce argument count
    • Document minimum requirements clearly
  3. Testing Edge Cases:

    • Always test with: zero, one, and many arguments
    • Test with slice unpacking

Practical Example

func join(sep string, strs ...string) string { if len(strs) == 0 { return "" } return strings.Join(strs, sep) } // Usage: join(", ", "a", "b", "c") // "a, b, c" words := []string{"hello", "world"} join(" ", words...) // "hello world"

Anti-Patterns to Avoid

  1. Hidden Allocations:

    // This creates a new slice allocation each call func badIdea(values ...int) { // ... }
  2. Type Safety Violations:

    // Avoid unconstrained interface{} without type checking func risky(values ...interface{}) { // Might panic if wrong types passed }

Key Characteristics to Remember

  1. Single Variadic Parameter Rule:

    • Only one variadic parameter allowed per function
    • Must be the last parameter in the signature
  2. Behind the Scenes:

    • Go converts variadic arguments into a slice
    • When no arguments passed, it’s an empty slice (not nil) len=0
  3. Slice Unpacking:

    • Pass existing slices using slice... syntax
    • Works for both array slices and slice literals
nums := []int{1, 2, 3} sum(nums...) // Equivalent to sum(1, 2, 3)