How to set default values in Go structs
Asked Answered
G

9

275

There are multiple answers/techniques to the below question:

  1. How to set default values to golang structs?
  2. How to initialize structs in golang

I have a couple of answers but further discussion is required.

Gazpacho answered 10/5, 2016 at 10:0 Comment(3)
Related: How to make sure that a method is used after an object is created in golang?Gingras
@Gingras You answer does give provide a way to do it but going by the Question Title, it is in no way similar or searchable since it is a very specific question. I will add the link in my answer though.Gazpacho
There are two questions here, pick one. Assuming you opt for the first question (as per question title), please be more specific about your prior research and where your other answers require more discusssion,.Lipoid
G
114
  1. Force a method to get the struct (the constructor way).

    From this post:

    A good design is to make your type unexported, but provide an exported constructor function like NewMyType() in which you can properly initialize your struct / type. Also return an interface type and not a concrete type, and the interface should contain everything others want to do with your value. And your concrete type must implement that interface of course.

    This can be done by simply making the type itself unexported. You can export the function NewSomething and even the fields Text and DefaultText, but just don't export the struct type something.

  2. Another way to customize it for you own module is by using a Config struct to set default values (Option 5 in the link). Not a good way though.

Gazpacho answered 30/5, 2016 at 9:19 Comment(5)
This is now a broken link (404): joneisen.tumblr.com/post/53695478114/golang-and-default-valuesDemocratic
It's available in the wayback machine.Dah
FWIW, I think it is 'Option 3' - at least in the wayback machine link. (There is no 'Option 5', there).Vicentevicepresident
The first quote has been modified to: "You may choose to make your type unexported, and provide an exported constructor function like NewMyType() in which you can properly initialize your struct / type. ... you can stop worrying about improper initialization."Olander
Here's a problem I ran into with this approach. ---- In the New() function you will have to add a parameter for every field in the struct, and if you have many fields this becomes cumbersome very fast for callers of New(). Obviously you can't pass an Options struct as an argument because then you end up with the same problem we are trying to solve. ---- Instead I opted to have no parameters to New(), it simply will return the struct with custom default values. The idea was that callers call New() and then set the fields after. But now since it's the interface they can't do that.Sunsunbaked
D
164

One possible idea is to write separate constructor function

//Something is the structure we work with
type Something struct {
     Text string 
     DefaultText string 
} 
// NewSomething create new instance of Something
func NewSomething(text string) Something {
   something := Something{}
   something.Text = text
   something.DefaultText = "default text"
   return something
}
Delectable answered 10/5, 2016 at 10:11 Comment(11)
Yes, this is one of the ways that I have also mentioned in my answer but there is no way we can force anyone to use this function only.Gazpacho
@Gazpacho it's either this or use an interface, which would be ugly and overcomplicated.Night
@Gazpacho yes, you can force people to use this constructor if you simply make the type itself unexported. You can export the function NewSomething and even the fields Text and DefaultText, but just don't export the struct type something.Synagogue
Worth Trying. Still wondering if the struct variable will be accessible directly. Will check it out. Thanks !!Gazpacho
@Amit, I have mention an extension to this in my answer - which recommends to make use of interface as a return valueGazpacho
The problem is worse... if a third party (library, for example) is used to instantiate your struct (via reflect.New(), for example), it couldn't be expected to know about your specially-named factory function. In that case, and short of the language itself being changed, only an interface (which the library could check for) would do, I think.Nutritious
Shouldn't you return &something with return type *Something as a good practice?Rorry
To Value add Amit's one, It will be better to go with singleton instead of generating the struct every timeLebron
It is good to set default, but sometimes, I might want to override default. In this case, I won't be able to initialize a struct with a value that is not the default. a little annoying for meTaker
@Nutritious how often does that come up? Do any of the stdlib functions do this for user defined types?Ryanryann
@AmitKumarGupta neat idea but making the type private means the receiver cannot use the type in any function calls (unless the receiver is in same package of course). So not a long-term solution (sooner or later you will have to expose it).Cowbird
G
114
  1. Force a method to get the struct (the constructor way).

    From this post:

    A good design is to make your type unexported, but provide an exported constructor function like NewMyType() in which you can properly initialize your struct / type. Also return an interface type and not a concrete type, and the interface should contain everything others want to do with your value. And your concrete type must implement that interface of course.

    This can be done by simply making the type itself unexported. You can export the function NewSomething and even the fields Text and DefaultText, but just don't export the struct type something.

  2. Another way to customize it for you own module is by using a Config struct to set default values (Option 5 in the link). Not a good way though.

Gazpacho answered 30/5, 2016 at 9:19 Comment(5)
This is now a broken link (404): joneisen.tumblr.com/post/53695478114/golang-and-default-valuesDemocratic
It's available in the wayback machine.Dah
FWIW, I think it is 'Option 3' - at least in the wayback machine link. (There is no 'Option 5', there).Vicentevicepresident
The first quote has been modified to: "You may choose to make your type unexported, and provide an exported constructor function like NewMyType() in which you can properly initialize your struct / type. ... you can stop worrying about improper initialization."Olander
Here's a problem I ran into with this approach. ---- In the New() function you will have to add a parameter for every field in the struct, and if you have many fields this becomes cumbersome very fast for callers of New(). Obviously you can't pass an Options struct as an argument because then you end up with the same problem we are trying to solve. ---- Instead I opted to have no parameters to New(), it simply will return the struct with custom default values. The idea was that callers call New() and then set the fields after. But now since it's the interface they can't do that.Sunsunbaked
K
62

One problem with option 1 in answer from Victor Zamanian is that if the type isn't exported then users of your package can't declare it as the type for function parameters etc. One way around this would be to export an interface instead of the struct e.g.

package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
    Name string
    Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
    return candidate{name, 0}  // enforce the default value here
}

Which lets us declare function parameter types using the exported Candidate interface. The only disadvantage I can see from this solution is that all our methods need to be declared in the interface definition, but you could argue that that is good practice anyway.

Keneth answered 10/8, 2017 at 9:10 Comment(2)
Do you then need "Candidate" to repeat "Name string" in the interface if you want "c.Name" to be accessible?Ricoricochet
Having Name and Votes upper-cased fields is confusing here. They might be useful for json.Marshal or similar reflect-based operations, but for normal access they are as good as lowercase fields. The interface cannot export fields, only methods (i.e. it needs methods to be usable).Denudation
S
46

There is a way of doing this with tags, which allows for multiple defaults.

Assume you have the following struct, with 2 default tags default0 and default1.

type A struct {
   I int    `default0:"3" default1:"42"`
   S string `default0:"Some String..." default1:"Some Other String..."`
}

Now it's possible to Set the defaults.

func main() {

ptr := &A{}

Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...

Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}

Here's the complete program in a playground.

If you're interested in a more complex example, say with slices and maps, then, take a look at creasty/defaultse

Scrip answered 26/6, 2019 at 18:21 Comment(2)
Thank's a lot! I started to write the same code as the library suggested and came across this post. It does exactly what you expect (github.com/creasty/defaults). If you have no value it sets the default, but if you assigned a value to your variable, then it's going to not assign the default. It works pretty well with the yaml.v2 library.Selfcontrol
This is a nice alternative, but it's not from golang. You have a generic constructor that use reflections. A real default value would be set automatically on every new struct instance.Jackson
T
7

What about making something like this:

// Card is the structure we work with
type Card struct {
    Html        js.Value
    DefaultText string `default:"html"` // this only works with strings
}

// Init is the main function that initiate the structure, and return it
func (c Card) Init() Card {
    c.Html = Document.Call("createElement", "div")
    return c
}

Then call it as:

c := new(Card).Init()
Trap answered 18/11, 2021 at 15:43 Comment(0)
M
6

From https://golang.org/doc/effective_go.html#composite_literals:

Sometimes the zero value isn't good enough and an initializing constructor is necessary, as in this example derived from package os.

    func NewFile(fd int, name string) *File {
      if fd < 0 {
        return nil
      }
      f := new(File)
      f.fd = fd
      f.name = name
      f.dirinfo = nil
      f.nepipe = 0
      return f
}
Marieann answered 2/12, 2017 at 4:38 Comment(0)
B
4

I found this thread very helpful and educational. The other answers already provide good guidance, but I wanted to summarize my takeaways with an easy to reference (i.e. copy-paste) approach:

package main

import (
    "fmt"
)

// Define an interface that is exported by your package.
type Foo interface {
  GetValue() string // A function that'll return the value initialized with a default.
  SetValue(v string) // A function that can update the default value.
}

// Define a struct type that is not exported by your package.
type foo struct {
  value string
}

// A factory method to initialize an instance of `foo`,
// the unexported struct, with a default value.
func NewFoo() Foo {
  return &foo{
    value: "I am the DEFAULT value.",
  }
}

// Implementation of the interface's `GetValue`
// for struct `foo`.
func (f *foo) GetValue() string {
  return f.value
}

// Implementation of the interface's `SetValue`
// for struct `foo`.
func (f *foo) SetValue(v string) {
  f.value = v
}

func main() {
  f := NewFoo()
  fmt.Printf("value: `%s`\n", f.GetValue())
  f.SetValue("I am the UPDATED value.")
  fmt.Printf("value: `%s`\n", f.GetValue())
}
Bobbiebobbin answered 19/1, 2022 at 18:34 Comment(1)
Just one note on this example: Go discourages putting Get into the getter's name. Instead your Foo interface could declare Value() string and SetValue(string). See go.dev/doc/effective_go#GettersTore
E
1

One way to do that is:

// declare a type
type A struct {
    Filed1 string
    Field2 map[string]interface{}
}

So whenever you need a new variable of your custom defined type just call the NewA function also you can parameterise the function to optionally assign the values to the struct fields

func NewA() *A {
    return &A{
        Filed1: "",
        Field2: make(map[string]interface{}),
    }
}
Edita answered 11/5, 2021 at 16:39 Comment(1)
if you export the type, the default constructor can be simply bypassedPaugh
C
-3

for set default values in Go structs we use anonymous struct:

Person := struct {
    name      string
    age       int
    city      string
}{
    name:      "Peter",
    age:        21,
    city:      "Noida",
}

fmt.Println(Person)

Cervicitis answered 5/10, 2021 at 11:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.