Optional Parameters in Go: 5 Working Approaches (From Simple to Advanced)

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

"Where are my optional parameters?" — The Cry of a Go Developer's Soul



You've just switched to Go from Python or JavaScript and are writing a function to send an email. In Python, it would look like this:



def send_email(to, subject, cc=None, bcc=None, attachments=None):    pass


It's simple: cc, bcc, and attachments are optional parameters. You can omit them, and the function will work with default values.



But here's the catch: Go has no method overloading and no named arguments with defaults. The Stack Overflow community confirms this: the question "Optional Parameters in Go?" gets thousands of views. This is a real pain point for beginners.



The good news: Go has at least 5 elegant ways to solve this problem. In this article, we'll cover them all — from simple to production-ready.



Why is Go so "Strict"?



The Go team deliberately omitted optional parameters to:

  • Simplify code readability — you always see how many arguments are passed to a function.
  • Avoid magical behavior — no unexpected defaults.
  • Speed up compilation — less syntactic sugar.


But this doesn't mean you can't make your code flexible. Let's see how to work around the limitations.



Approach 1: Variadic Functions — "Cheap and Cheerful"



The simplest way is to use the ellipsis ... to pass a variable number of arguments of the same type.



package main

import "fmt"

// Sum accepts any number of integersfunc Sum(nums ...int) int { total := 0 for _, n := range nums { total += n } return total}

func main() { fmt.Println(Sum(1, 2, 3)) // 6 fmt.Println(Sum(10, 20)) // 30 fmt.Println(Sum()) // 0 — also works}


Pros: simplicity, built-in language support.
Cons: all arguments must be of the same type. You can't say "pass a string, then an optional int".



Variadic functions in Go are great for loggers, sum functions, and any operations where the amount of data is unknown in advance.



Approach 2: Configuration Struct — "When There Are Many Parameters"



If you have more than 2-3 optional parameters of different types — use a struct.



package main

import "fmt"

type EmailConfig struct { CC []string BCC []string Attachments []string IsHTML bool}

func SendEmail(to, subject, body string, cfg EmailConfig) { fmt.Printf("To: %s, Subject: %s", to, subject) if cfg.IsHTML { fmt.Println("HTML mode enabled") } if len(cfg.CC) > 0 { fmt.Printf("CC: %v", cfg.CC) }}

func main() { // Use default values (zero values) SendEmail("user@example.com", "Hello", "Body", EmailConfig{})

// Override some fields cfg := EmailConfig{ CC: []string{"boss@example.com"}, IsHTML: true, } SendEmail("user@example.com", "Hello", "Body", cfg)}


Note: zero values in Go (empty string, 0, nil for slices) become the default values. This is our implementation of optional parameters in Go using structs.



Pros: readable, typed, easy to extend.
Cons: you need to create a struct every time, even if you're not changing anything.



Approach 3: Functional Options Pattern — "The Go Gold Standard"



This pattern was invented by Dave Cheney, one of the key Go developers. It is used in libraries such as gRPC, the AWS SDK, and Go's own standard library (e.g.,

Blogs

Book Recommendations