Default value in Go's method
Asked Answered
C

4

238

Is there a way to specify default value in Go's function? I am trying to find this in the documentation but I can't find anything that specifies that this is even possible.

func SaySomething(i string = "Hello")(string){
...
}
Capercaillie answered 26/10, 2013 at 22:10 Comment(0)
P
180

No, the powers that be at Google chose not to support that.

https://groups.google.com/forum/#!topic/golang-nuts/-5MCaivW0qQ

Philosophize answered 26/10, 2013 at 22:43 Comment(1)
Also this discussion with official statement and this related question.Cannonry
E
260

NO,but there are some other options to implement default value. There are some good blog posts on the subject, but here are some specific examples.

Option 1: The caller chooses to use default values

// Both parameters are optional, use empty string for default value
func Concat1(a string, b int) string {
  if a == "" {
    a = "default-a"
  }
  if b == 0 {
    b = 5
  }

  return fmt.Sprintf("%s%d", a, b)
}

Option 2: A single optional parameter at the end

// a is required, b is optional.
// Only the first value in b_optional will be used.
func Concat2(a string, b_optional ...int) string {
  b := 5
  if len(b_optional) > 0 {
    b = b_optional[0]
  }

  return fmt.Sprintf("%s%d", a, b)
}

Option 3: A config struct

// A declarative default value syntax
// Empty values will be replaced with defaults
type Parameters struct {
  A string `default:"default-a"` // this only works with strings
  B string // default is 5
}

func Concat3(prm Parameters) string {
  typ := reflect.TypeOf(prm)

  if prm.A == "" {
    f, _ := typ.FieldByName("A")
    prm.A = f.Tag.Get("default")
  }

  if prm.B == 0 {
    prm.B = 5
  }

  return fmt.Sprintf("%s%d", prm.A, prm.B)
}

Option 4: Full variadic argument parsing (javascript style)

func Concat4(args ...interface{}) string {
  a := "default-a"
  b := 5

  for _, arg := range args {
    switch t := arg.(type) {
      case string:
        a = t
      case int:
        b = t
      default:
        panic("Unknown argument")
    }
  }

  return fmt.Sprintf("%s%d", a, b)
}
Equilibrate answered 14/5, 2014 at 9:13 Comment(5)
What a pain. I wish it was: func Concat1(a string = 'foo', b int = 10) string { like in most other modern languages... It would reduce any of the given examples pretty much to one line of code.Ayer
Do we have an option of writing two different functions and leave it to the caller to be explicit about what they expect.Tryck
@Tryck no, golang doesn't allow function overloading Does the Go language have function/method overloading?Songful
Default values are tricky, when you have a caller that omits the value - you never know if the value they want was "default at the moment of implementation" or "default at any point in the future" - it makes the code intent less obvious. Go making it painful might not be as bad as you think.Crispa
The lack of default parameter values, and named parameters, is yet another shortcoming of Go.Telescopy
P
180

No, the powers that be at Google chose not to support that.

https://groups.google.com/forum/#!topic/golang-nuts/-5MCaivW0qQ

Philosophize answered 26/10, 2013 at 22:43 Comment(1)
Also this discussion with official statement and this related question.Cannonry
A
18

No, there is no way to specify defaults. I believer this is done on purpose to enhance readability, at the cost of a little more time (and, hopefully, thought) on the writer's end.

I think the proper approach to having a "default" is to have a new function which supplies that default to the more generic function. Having this, your code becomes clearer on your intent. For example:

func SaySomething(say string) {
    // All the complicated bits involved in saying something
}

func SayHello() {
    SaySomething("Hello")
}

With very little effort, I made a function that does a common thing and reused the generic function. You can see this in many libraries, fmt.Println for example just adds a newline to what fmt.Print would otherwise do. When reading someone's code, however, it is clear what they intend to do by the function they call. With default values, I won't know what is supposed to be happening without also going to the function to reference what the default value actually is.

Attaint answered 14/9, 2017 at 15:8 Comment(4)
I like and agree with this answer but damn do I still wish they at least had an obtuse but idiomatic way of doing it.Stratopause
The "just add another function" argument can result in an explosion of methods required to deal with multiple permutations, requires the burden of deciding on meaningful, discoverable and rememberable names for those methods, and adds burden to learning the packages that use more methods vs. less. #jmtcwCarrousel
Well, as someone who has had to untangle mountains of source code 10-20years later to find bugs to support customers, I think additional function names might have some benefitMartymartyn
There’s a gap between "this approach might have some benefit" and "let's design the language so that this approach is the only possible one (even where it doesn’t make sense)".Knucklehead
O
4

A default function argument usually provides the client code with a very common argument value while still allowing the client code to override it. A similar result can be achieved by turning the function into a method of a struct and the default argument into a field of that struct.

Let's see it with a function based on your example (it doesn't compile):

func SaySomething(msg string = "Hello") string {
    return fmt.Sprintf("I say %q", msg)
}

First, define a struct that will hold the default argument:

type SomethingSayer struct {
    msg string
}

Second, turn the function SaySomething into a method on the struct SomethingSayer. The original function parameter msg is replaced by s.msg, a SomethingSayer's field:

func (s SomethingSayer) SaySomething() string {
    return fmt.Sprintf("I say %q", s.msg)
}

You can also define a constructor for the SomethingSayer struct. This will set the field msg with the original default argument:

func NewSaySomethingSayer() SomethingSayer {
    return SomethingSayer {
        msg: "Hello", // the default argument
    }
}

Finally, define a convenience wrapping function that is like the original one but without the parameter that had the default argument:

func SaySomething() string {
    return NewSaySomethingSayer().SaySomething()
}

This way, the client code can just call SaySomething() when the defaults are suitable. If that's not the case, it can still construct its own SomethingSayer and then call the SaySomething() method:

fmt.Println(SaySomething())    // I say "Hello"

s := SomethingSayer {
    msg: "Bye",
}
fmt.Println(s.SaySomething())  // I say "Bye"

Note that this approach is also followed by the standard library in http.Get, http.Head, and http.Post. That is, this function allows you to perform a GET request without explicitly constructing a http.Client: it uses a globally available http.Client value (http.DefaultClient) and calls its Get method on it. Similarly, client code can still override the default arguments (e.g., the timeout) by constructing its own http.Client and calling Get on it.

Oilbird answered 1/1, 2023 at 13:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.