Module lessons (5/5)
The `New...` constructor pattern
Go has no constructor keyword. The convention is a factory
function that returns a properly initialized value or pointer. By
name it is typically called NewT (or NewXxx).
Base pattern: NewT
type User struct {
Name string
Age int
}
func NewUser(name string, age int) *User {
return &User{Name: name, Age: age}
}
u := NewUser("Ada", 36)
fmt.Println(u.Name)Benefits over the direct literal &User{...}:
- You centralize validation and defaults.
- You can have unexported fields and populate them only from the constructor.
- You can change the implementation without breaking callers.
Constructor with validation: (*T, error)
When initialization can fail, return (*T, error):
import "errors"
func NewUser(name string, age int) (*User, error) {
if name == "" {
return nil, errors.New("nome obbligatorio")
}
if age < 0 {
return nil, errors.New("eta non valida")
}
return &User{Name: name, Age: age}, nil
}
u, err := NewUser("Ada", 36)
if err != nil {
// handle...
}This is the idiomatic pattern for any constructor with preconditions: db connection, file handle, server config, etc.
Multiple constructors
If you have multiple "ways" to construct, use descriptive names:
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }No overloading in Go: the name disambiguates.
Functional options (advanced glimpse)
When there are many optional parameters, the idiomatic pattern is "functional options":
type Option func(*Server)
func WithPort(p int) Option {
return func(s *Server) { s.Port = p }
}
func WithTLS(cert, key string) Option {
return func(s *Server) { s.Cert, s.Key = cert, key }
}
func NewServer(opts ...Option) *Server {
s := &Server{Port: 8080} // default
for _, opt := range opts {
opt(s)
}
return s
}
s := NewServer(WithPort(9000), WithTLS("c.pem", "k.pem"))It is more verbose than a literal but scales well with many optional
parameters and preserves backward compatibility (adding a new Option
does not break existing callers).
When you do NOT need a constructor
If the type's zero value is already useful, let the caller use the
direct literal. Canonical example: sync.Mutex has no constructor,
var m sync.Mutex is already a ready-to-use mutex. Same for bytes.Buffer.
The rule is: "Make the zero value useful". If you can, no mandatory constructor.
Try it
Write the factory function NewUser(name string, age int) *User that returns &User{Name: name, Age: age}.
Show hint
Use the literal `&User{...}` to return a pointer.
Solution available after 3 attempts
Turn NewUser into (name string) (*User, error): if name == '' return nil, errors.New('nome obbligatorio').
Show hint
If there are preconditions, return (*T, error) and return nil + error on failure.
Solution available after 3 attempts
What is the idiomatic convention for constructors in Go?
// Which signature is more idiomatic?Recap
- No
constructor: use aNewTfunction (orNewinside the same-named package). - Return
*Tfor the simple case;(*T, error)with preconditions or validation. - On error: return
nilas the pointer and the explanatory error as the second value. - No overloading; use descriptive names (
NewUserFromJSON,NewAdmin). - "Make the zero value useful": if you can, the constructor is unnecessary.
- Advanced pattern for many optional parameters: functional options.