Go by default not support set default and optional argument in a function. The simple approach is using variadic argument but only support 1 data type for example func createSomething(input1, input2variadic...string)
To supporting multiple type argument in a function you can use functional options pattern, basically variadic argument with type function. The goals is like code example below
package main
import (
"log"
"github.com/xx/yy/api"
)
func main() {
srv := api.NewServer(
server.WithHost("lumochift.org"),
server.WithPort(3000),
)
if err := srv.Start(); err != nil {
log.Fatal(err)
}
}
package api
type Server struct{
host string
port int
}
func NewServer(options ...func(*Server)) *Server {
srv := &Server{
host: "localhost",
port: 8080,
}
for _, o := range options {
o(srv)
}
return srv
}
func (s *Server) Start() error {
// todo
}
func WithHost(host string) func(*Server) {
return func(s *Server) {
s.host = host
}
}
func WithPort(port int) func(*Server) {
return func(s *Server) {
s.port = port
}
}
Using functional options pattern you can select the necessary options only, even without argument (server.New()
) the struct creation is valid.
The main problem, if the struct is big we must implement all optional function. Fortunately I already build simple tools for generate optional function.
Install on your system using command go install github.com/lumochift/optgen@latest
.
Let’s expand Server
struct above with additional config timeout
and maxConn
.
type Server struct {
host string `opt`
port int `opt`
timeout time.Duration
maxConn int
}
Then generate functional options implementation using command optgen -file server.go -name Server -w
. Don’t forget use -w
to append implementation to the selected file server.go
. The result only generate function SetHost
and SetPort
only because declared opt
in the struct tag just for host
and port
.
type Server struct {
host string `opt`
port int `opt`
timeout time.Duration
maxConn int
}
func NewServer(options ...func(*Server)) (*Server, error) {
server := &Server{}
for _, option := range options {
option(server)
}
return server, nil
}
func SetHost(host string) func(*Server) {
return func(c *Server) {
c.host = host
}
}
func SetPort(port int) func(*Server) {
return func(c *Server) {
c.port = port
}
}
To generate all fields as optional parameter use flag -all
for example optgen -file server.go -name Server -w -all
, previous generated code must be deleted manually 1
type Server struct {
host string `opt`
port int `opt`
timeout time.Duration
maxConn int
}
// NewServer returns a new Server.
func NewServer(options ...func(*Server)) (*Server, error) {
// Prepare a Server with default host.
server := &Server{}
// Apply options.
for _, option := range options {
option(server)
}
// Do anything here
return server, nil
}
// SetHost sets the Host
func SetHost(host string) func(*Server) {
return func(c *Server) {
c.host = host
}
}
// SetPort sets the Port
func SetPort(port int) func(*Server) {
return func(c *Server) {
c.port = port
}
}
// SetTimeout sets the Timeout
func SetTimeout(timeout time.Duration) func(*Server) {
return func(c *Server) {
c.timeout = timeout
}
}
// SetMaxConn sets the MaxConn
func SetMaxConn(maxconn int) func(*Server) {
return func(c *Server) {
c.maxConn = maxconn
}
}
Footnotes
The limitation
optgen
CLI only append code, not replace previously generated code. ↩