1

Given the following:

type AStruct struct {
    m_Map map[int]bool
}

In this case, an instance ofAStructcannot be used untilAStruct.m_Mapis initialized:

m_Map=make(map[int]bool,100)

I have taken to writing an Init()function for my structs in such cases:

func (s *AStruct) Init() {
    s.m_Map=make(map[int]bool,100)
}

I don't particularly care for this design, because it requires(s *AStruct) Init() to be public and mandates that clients call it explicitly before using an instance of AStuct - in the interim an unusable instance ofAStuctis out there, waiting to generate apanic.

I could make init() private and declare an initialized boolflag in thestruct set ittrueininit()after everything is initialized and check in each method:

func (s *AStruct) DoStuff {

    if !s.initialized {
         s.init()
    }

    s.m_Map[1]=false
    s.m_Map[2]=true
}

But this is awkward and adds superfluous code.

Is there is standard way of handling this in Go? One that guarantees m_Map will be initialized without relying on clients to call Init()?

Vector
  • 10,879
  • 12
  • 61
  • 101

1 Answers1

10

The standard for a type Foo is to write a constructor NewFoo() which is not a method on the type, but returns a value of type Foo (or *Foo). Then as long as all instances of Foo are generated through a call to NewFoo() (and not through a literal or a call to the new() builtin) then you're safe.

Evan
  • 6,369
  • 1
  • 29
  • 30
  • 8
    The exception to this is if type `Foo` is the only exported type in package `foo`, in which case you can simply call the constructor `New` so that users write `foo.New()` instead of `foo.NewFoo()`. – Evan Mar 29 '14 at 21:20
  • _constructor_ - I'd call it a factory, but that's fine. Good design mandates that all objects should be acquired through factories, and in a garbage collected language, where there is no problem of ownership and knowing when to delete an object reference, it's very simple to implement. Definitely the way to do it. – Vector Mar 29 '14 at 23:08
  • 2
    @comeAndGo, consider looking at the standard Go library -- it makes heavy use of this approach. – kostix Mar 30 '14 at 09:40
  • 1
    @comeAndGo, please also read the "Names" and "Data, allocation with new" chapters of ["Effective Go"](http://golang.org/doc/effective_go.html) – kostix Mar 30 '14 at 09:46
  • @kostix - I should read the whole thing, but I hate reading online books. Since it's filled with links, I can't download it and make it into a working epub unless I'm online with my ereader, which I'm usually not. Maybe there is a full version out there someplace that someone knows about? I definitely need to spend time looking at the Standard Library source code now that I'm fairly comfortable reading through Go code. – Vector Mar 30 '14 at 21:15
  • @comeAndGo, AFAIK most links it contains are in the ToC so they reference the document itself. I've read it offline (though by saving it "for later read" in the browser on my tablet). That's what I do for most Go-related documents of interest anyway (mostly posts on http://blog.golang.org and [Dave Cheney's blog](http://dave.cheney.net/category/golang) to read them while commuting. – kostix Apr 01 '14 at 09:44
  • @comeAndGo, and while we're on it, I'd also recommend to read [Go for C++ programmers](http://code.google.com/p/go-wiki/wiki/GoForCPPProgrammers). Even though you have Delphi background, most things should be familiar, and contrasting and comparing the approaches of a platform with another popular one greatly helps with understanding the former's underpinnings IMO. – kostix Apr 01 '14 at 09:48
  • @kostix - this looks like something that will help me - Tnx - although I am getting the hang of the Go mindset, slowly but surely. I have also worked a good deal in C++ just that my "bread butter" has been delphi for close to 20 years. But I use Delphi as a systems language, just as someone would use C++ - I never use components, etc - if have a component I treat it as a library - and most of my work is middle-ware or server side - no GUI etc. Delphi is very simlilar to C++ in many ways. So I will take the time to through that page. – Vector Apr 01 '14 at 11:11
  • "The standard for a type Foo is to write a constructor NewFoo()" is only true as long as there is no stuttering. If the package is named "foo" then it's definitely enough for the constructor to be named "New": `foo.New()`. See this answer: https://stackoverflow.com/a/18125763/1623885 – George Aristy Aug 18 '19 at 19:18