297

I have a struct and I would like it to be initialised with some sensible default values.

Typically, the thing to do here is to use a constructor but since go isn't really OOP in the traditional sense these aren't true objects and it has no constructors.

I have noticed the init method but that is at the package level. Is there something else similar that can be used at the struct level?

If not what is the accepted best practice for this type of thing in Go?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Marty Wallace
  • 34,046
  • 53
  • 137
  • 200
  • try to use this library on github: https://github.com/mobiletoly/gobetter I made it so to generate required fields and provide ready-to-use constructors – interrupt Jun 25 '21 at 15:33

11 Answers11

300

There are some equivalents of constructors for when the zero values can't make sensible default values or for when some parameter is necessary for the struct initialization.

Supposing you have a struct like this :

type Thing struct {
    Name  string
    Num   int
}

then, if the zero values aren't fitting, you would typically construct an instance with a NewThing function returning a pointer :

func NewThing(someParameter string) *Thing {
    p := new(Thing)
    p.Name = someParameter
    p.Num = 33 // <- a very sensible default value
    return p
}

When your struct is simple enough, you can use this condensed construct :

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33}
}

If you don't want to return a pointer, then a practice is to call the function makeThing instead of NewThing :

func makeThing(name string) Thing {
    return Thing{name, 33}
}

Reference : Allocation with new in Effective Go.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 8
    Ok so this makes sense but it implies that clients of these must know about the New and make functions. i.e. this is not standard among all structs. I imagine that can be handled with interfaces – Marty Wallace Aug 08 '13 at 12:30
  • 3
    I'm not sure of what you mean. Having a NewThing function is something standard. If you mean they're not automatically called, yes, but you can't automatically use structs anyway. I don't think you should try to hide those constructors with interfaces, the code is clearer when they appear. – Denys Séguret Aug 08 '13 at 12:31
  • 31
    It's not very common to allocate structs with `new` and set the values afterwards. Struct literals are the preferred way there. And I am not sure about your "makeThing" naming convention either. The standard library calls the constructors New() or NewThing() consistently and I have never encountered any makeThing() functions my self... – tux21b Aug 08 '13 at 12:38
  • @tux21b well... [EffectiveGo](http://golang.org/doc/effective_go.html#allocation_new) gives an example of a `NewFile` constructor using `new(File)`... – Denys Séguret Aug 08 '13 at 12:41
  • 5
    Yes, but the following paragraph of "Effective Go" introduces struct literals and demonstrate how they can be used to write a more idiomatic version of the `NewFile` constructor :) – tux21b Aug 08 '13 at 12:47
  • @tux21b You're right. And I admit it's often clearer to use a struct literal. But you'll also find new(Thing) in standard packages. But I'll edit a little my answer. – Denys Séguret Aug 08 '13 at 12:49
  • `new` is normally used if you do not want to initialize anything. A common example is `c := new(math.Big).Add(a, b)` which would look extremely ugly with struct literals... – tux21b Aug 08 '13 at 12:51
  • Is it preferrable to return a pointer or the struct, and why? – Ivan Feb 29 '16 at 15:26
  • @Ivan [It depends](http://stackoverflow.com/questions/13760793/go-tour-when-to-not-use-pointer-to-struct-literal-in-a-variable) – Denys Séguret Feb 29 '16 at 15:44
  • I think this approach is called "Factory function". – golopot Feb 10 '18 at 14:12
  • 5
    I can't help myself, but this goes against the principals of encapsulation. In a packet that consists of several "classes" (that is what a concept like a struct + methods is, no matter how you arrange it) one should not deal with naming conflicts anymore. But in this glory go approach, you basically now do not know which function takes the constructor role as a factory. So you end up with naming conventions like "NewClass". Go is bad coding style. – Chris Pillen Feb 08 '19 at 19:37
  • @MartyWallace Go's official package `bufio` shows the philosophy (if we agree that official package is the best practice). `bufio` defines `ReadWriter` struct type, which is exported. So the user could just do `rw := &bufio.ReadWriter{reader, writer}`, and ignore the provided function `rw := bufio.NewReadWriter(reader, writer)`. I believe this means: the package user has the responsibility to read the documentation of the package and call the correct functions to create new (correctly initialized) objects. – yyFred Dec 25 '20 at 07:08
  • This answer asserts that `makeThing` is a naming convention. Where has this assertion come from? – Burhan Ali Apr 09 '23 at 18:45
176

There are actually two accepted best practices:

  1. Make the zero value of your struct a sensible default. (While this looks strange to most people coming from "traditional" oop it often works and is really convenient).
  2. Provide a function func New() YourTyp or if you have several such types in your package functions func NewYourType1() YourType1 and so on.

Document if a zero value of your type is usable or not (in which case it has to be set up by one of the New... functions. (For the "traditionalist" oops: Someone who does not read the documentation won't be able to use your types properly, even if he cannot create objects in undefined states.)

Volker
  • 40,468
  • 7
  • 81
  • 87
  • 7
    how would that work for properties such as maps. The default value for this is nil, right? Therefore should these always be initialised via a New function? – Marty Wallace Aug 08 '13 at 13:36
  • 3
    Yes and no, it depends. Most probably yes, provide a `func New() T`. But depending on the case you could check for this nil map and `make` one only once it is needed. In this case: Document if this map creation is safe for concurrent use (aka your code which makes the map is guarded e.g. by a mutex.). Depends a bit whether the map is exported or not... Hard to tell without seeing code. – Volker Aug 08 '13 at 14:09
57

Go has objects. Objects can have constructors (although not automatic constructors). And finally, Go is an OOP language (data types have methods attached, but admittedly there are endless definitions of what OOP is.)

Nevertheless, the accepted best practice is to write zero or more constructors for your types.

As @dystroy posted his answer before I finished this answer, let me just add an alternative version of his example constructor, which I would probably write instead as:

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33} // <- 33: a very sensible default value
}

The reason I want to show you this version is that pretty often "inline" literals can be used instead of a "constructor" call.

a := NewThing("foo")
b := &Thing{"foo", 33}

Now *a == *b.

oneself
  • 38,641
  • 34
  • 96
  • 120
zzzz
  • 87,403
  • 16
  • 175
  • 139
  • 6
    +1 because of the equality thing. It might be a little (or totally) off-topic but it's important. I think it came with Go1, no ? – Denys Séguret Aug 08 '13 at 12:28
  • 7
    Can you add more explain about why a and b will be equal? – lazywei Oct 19 '13 at 14:51
  • 8
    @lazywei a != b, but *a == *b because the structs they point to have equal fields, see http://play.golang.org/p/A3ed7wNVVA for an example – FMJaguar Jun 23 '14 at 19:44
  • @lazywei Does it check shallow equality or deep equality? (For example, if `Thing` contained a map, it would be preferable to check deep equality.) – Cameron Hudson Jun 06 '21 at 15:02
  • 1
    As @CameronHudson says, if a struct contains a pointer then their "sturct" values may not be equal in the case mentioned, making *a == *b not necessarily true, depending what types are in the struct - and that is a matter for a "deep equality" check – Spacen Jasset Dec 08 '22 at 15:15
15

There are no default constructors in Go, but you can declare methods for any type. You could make it a habit to declare a method called "Init". Not sure if how this relates to best practices, but it helps keep names short without loosing clarity.

package main

import "fmt"

type Thing struct {
    Name string
    Num int
}

func (t *Thing) Init(name string, num int) {
    t.Name = name
    t.Num = num
}

func main() {
    t := new(Thing)
    t.Init("Hello", 5)
    fmt.Printf("%s: %d\n", t.Name, t.Num)
}

The result is:

Hello: 5
Sebastian Bartos
  • 2,317
  • 1
  • 20
  • 20
  • 3
    Question: semantically, `t := new(Thing) \n t.Init(...)` is the same as `var t Thing \n t.Init(...)`, right? Which of the forms is seen as more idiomatic in Go? – Gwyneth Llewelyn Jul 02 '17 at 22:22
14

I like the explanation from this blog post:

The function New is a Go convention for packages that create a core type or different types for use by the application developer. Look at how New is defined and implemented in log.go, bufio.go and cypto.go:

log.go

// New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
    return &Logger{out: out, prefix: prefix, flag: flag}
}

bufio.go

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader {
    return NewReaderSize(rd, defaultBufSize)
}

crypto.go

// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
    if h > 0 && h < maxHash {
        f := hashes[h]
        if f != nil {
            return f()
        }
    }
    panic("crypto: requested hash function is unavailable")
}

Since each package acts as a namespace, every package can have their own version of New. In bufio.go multiple types can be created, so there is no standalone New function. Here you will find functions like NewReader and NewWriter.

danilopopeye
  • 9,610
  • 1
  • 23
  • 31
Ivan Aracki
  • 4,861
  • 11
  • 59
  • 73
  • 2
    The log and bufio examples appear to be functions that return pointers, while the crypto Hash appears to be a constructor method more like what you would expect in other OOP languages like Java. The Hash New() method also doesn't return a pointer, it returns a new Hash. In that sense it looks more like a factory than an initializer. I am just wondering about this because using a _new_ function with any sort of complexity in it makes embedding types lose that connection to their constructor, or forces you to reimplement it if you want that pseudo-inheritence to be maintained. – Davos May 02 '19 at 04:27
9

In Go, a constructor can be implemented using a function that returns a pointer to a modified structure.

type Colors struct {
    R   byte
    G   byte
    B   byte
}

// Constructor
func NewColors (r, g, b byte) *Colors {
    return &Color{R:r, G:g, B:b}
}

For weak dependencies and better abstraction, the constructor does not return a pointer to a structure, but an interface that this structure implements.

type Painter interface {
    paintMethod1() byte
    paintMethod2(byte) byte
}

type Colors struct {
    R byte
    G byte
    B byte
}

// Constructor return intreface
func NewColors(r, g, b byte) Painter {
    return &Color{R: r, G: g, B: b}
}

func (c *Colors) paintMethod1() byte {
    return c.R
}

func (c *Colors) paintMethod2(b byte) byte {
    return c.B = b
}
  • 8
    I don't think returning an interface is best practice. You usually want to accept an interface and return a pointer to a struct (possibly implementing an interface). It's still testable just the same. The calling code must treat the return value as the interface type. This way mocks can be assigned to it transparently. – erikbozic Feb 10 '20 at 22:44
6

another way is;

package person

type Person struct {
    Name string
    Old  int
}

func New(name string, old int) *Person {
    // set only specific field value with field key
    return &Person{
        Name: name,
    }
}
Kerem
  • 11,377
  • 5
  • 59
  • 58
  • 1
    shouldn't it be NewPerson instead of New ? – DevX Jun 21 '18 at 09:25
  • 6
    @DevX no, because that's the main (possibly only) type of the package. You'd use it as `person.New(name, old)`. Compare with `person.NewPerson(name, old)`, which stutters. – Filip Haglund Jul 11 '18 at 13:19
  • 1
    @FilipHaglund but the function New is not a method of Person Struct so you cant call person.New – DevX Jul 12 '18 at 06:12
  • @FilipHaglund I agree with this though: "no, because that's the main (possibly only) type of the package. " – DevX Jul 12 '18 at 06:13
  • 3
    person wasn't a variable, but the package :) So New is a func, not a method. – Filip Haglund Jul 12 '18 at 07:26
5

If you want to force the factory function usage, name your struct (your class) with the first character in lowercase. Then, it won't be possible to instantiate directly the struct, the factory method will be required.

This visibility based on first character lower/upper case work also for struct field and for the function/method. If you don't want to allow external access, use lower case.

guillaume blaquiere
  • 66,369
  • 2
  • 47
  • 76
  • 1
    You should leave that up to the developer who uses your types. Making a type you need to use outside your package private, and only offer factories can be rather inconvenient. The convention is to use a factory if available and otherwise you probably know what you're doing. – Dynom May 06 '20 at 11:15
  • 3
    @dynom, I understand your point. But isn't there a huge risk that the developer will instantiate my struct and forget to (or not know to) call the constructor? Therefore, each of my methods that receive such a struct must check to make sure the instance is initialized. – Robert Lugg Jun 03 '20 at 14:41
  • If a developer choses your type over the factories you provided, it's up to them to deal with the consequences. You can't envision all what a person might do with your code, so don't try to. Assume the person working with your code is smart enough to make that decision. Especially when writing tests, it's very pleasant you can stub/mock only what's needed. You shouldn't take that away from people. – Dynom Jun 16 '20 at 07:46
1

Golang is not OOP language in its official documents. All fields of Golang struct has a determined value(not like c/c++), so constructor function is not so necessary as cpp. If you need assign some fields some special values, use factory functions. Golang's community suggest New.. pattern names.

1

Many statements say "there's no default constructor in golang", but we can have this code, that compiles and runs. I think this is what a go novice would call default constructors

package main

import (
    "fmt"
)

type MyInt int

type Person struct {
    Name string
    Age  int
}

func main() {
    a := MyInt(10)
    fmt.Println(a)

    p := Person{"Jack", 11}
    fmt.Println(p)
}
ctrl-alt-delor
  • 7,506
  • 5
  • 40
  • 52
-3

I am new to go. I have a pattern taken from other languages, that have constructors. And will work in go.

  1. Create an init method.
  2. Make the init method an (object) once routine. It only runs the first time it is called (per object).
func (d *my_struct) Init (){
    //once
    if !d.is_inited {
        d.is_inited = true
        d.value1 = 7
        d.value2 = 6
    }
}
  1. Call init at the top of every method of this class.

This pattern is also useful, when you need late initialisation (constructor is too early).

Advantages: it hides all the complexity in the class, clients don't need to do anything.

Disadvantages: you must remember to call Init at the top of every method of the class.

ctrl-alt-delor
  • 7,506
  • 5
  • 40
  • 52