Golang function default value - go

I'm trying to assign default value using struct, but how do I distinguish if it is zero value or the actual input value without using pointer?
type Options struct {
a bool `default:"true"`
// other options
}
func processOptions(options *Options) {
if !options.a { // zero value - false
options.a = true // what if the user actually input false?
}
}
func test(options Options) {
processOptions(&options)
// ...
}
the pointer approach will be
type Options struct ...
func processOptions(options *Options) {
if options.a == nil {
aDefault := true
options.a = &aDefault
}
}
func test(options Options) ...
a := false
options := Options{&a}
test(options)
functional options mentioned by #maja takes advantages of variadic functions and struct type to make the 'constructor' initialize options safely regarding the types of each option.
type Options struct {
a bool
// other options
}
type Option func(*Options)
func WithA(a bool) Option {
return func(options *Options) {
options.a = a
}
}
func NewOptions(opts ...Option) *Options {
options := &Options{
a: true // default value
}
for _, opt := range opts {
opt(options)
}
return options
}
func test(options Options) {
// ...
}
options := NewOptions(
WithA(false),
)
test(*options)

In many cases there is no need to distinguish between a default value, or the value being explicitly set to zero.
Otherwise, you could use a pointer, or another field as a presence flag
type Option struct {
a bool
aProvided bool
}
func processOption(option *Option) {
if !option.aProvided
option.a = true
}
}
This would require callers to initialize this extra field as well as a. To reiterate my above advice, it is often easier if you don't need to distinguish.

Looks like you already solve it your self using *string. But you want to avoid that ...
Why don't you make your all your options are default as it is. for example you have options auto=true and repeat=false. You can rename your options for default value like auto to disableAuto
type Option struct {
Repeat bool // because repeat by default false
DisableAuto bool // because auto by default true, this options purpose is to disabled auto
}
Updated:
or maybe you want functional options patterns, here my example https://play.golang.org/p/4DsSaQQkWg0
for more reference you can see here https://golang.cafe/blog/golang-functional-options-pattern.html

Related

How to have multi options list for one function?

I am using options pattern to configure the behavior of function, there are two function Foo and Bar which one dependent on the other, the code as follow
type fooOptions struct {
fooName string
}
type FooOptions func(*fooOptions)
func WithFooName(fooName string) {
return func(options *fooOptions) {
options.fooName = fooName
}
}
func Foo(options ...FooOptions) {
opts := &fooOptions{}
for _, opt := range options {
opt(opts)
}
// do something
}
type barOptions struct {
barName string
}
type BarOptions func(*barOptions)
func WithBarName(barName string) {
return func(options *barOptions) {
options.barName = barName
}
}
// compile error,unable have multi options list
func Bar(boptions ...BarOptions,fOptions ...FooOptions) {
bopts := &barOptions{}
for _, opt := range boptions {
opt(bopts)
}
Foo(fOptions...)
}
I wonder how can I have multi options list for the Bar function,or any other smart way, so I can reuse the code, otherwise I have to flatten fooOptions into barOptions and writing the withXXX again

How to check if a value implements an interface

I want to compare my type by the specific way. For this purpose, I create the function MyType.Same(other MyType) bool for each type.
In some generic function, I want to check if the parameter has a function "Same" and invoke it if yes.
How can I do it in a generic way for different types?
type MyType struct {
MyField string
Id string // ignored by comparison
}
func (mt MyType) Same(other MyType) bool {
return mt.MyField == other.MyField
}
// MyOtherType... Same(other MyOtherType)
type Comparator interface {
Same(Camparator) bool // or Same(interface{}) bool
}
myType = new(MyType)
_, ok := reflect.ValueOf(myType).Interface().(Comparator) // ok - false
myOtherType = new(myOtherType)
_, ok := reflect.ValueOf(myOtherType).Interface().(Comparator) // ok - false
The types do not satisfy the Comparator interface. The types have a Same method, but those methods do not have argument type Comparator. The argument types must match to satisfy an interface.
Change the the methods and interface to take the same argument type. Use a type assertion to check that receiver and argument have the same type and to get argument as the receiver's type.
type Comparator interface {
Same(interface{}) bool
}
func (mt MyType) Same(other interface{}) bool {
mtOther, ok := other.(MyType)
if !ok {
return false
}
return return mt.MyField == mtOther.MyField
}
Use the following to compare two values:
func same(a, b interface{}) bool {
c, ok := a.(Comparator)
if !ok {
return false
}
return c.Same(b)
}
If the types the application works with have the Compare method, then there's no need to declare the Comparator interface or use the same function in the previous snippet of code. For example, the Comparator interface is not required for the following:
var mt MyType
var other interface{}
eq := mt.Same(other)

How can I access the fields of an interface in Go?

I'm trying to do this:
if event.Type == sdl.QUIT {
utils.Running = false
}
But I can't because when I try to build, I get this error:
./mm.go:11: event.Type undefined (type sdl.Event has no field or method Type)
Here is the relevant source code of the library I'm trying to use:
type Event interface{}
type CEvent struct {
Type uint32
_ [52]byte // padding
}
type CommonEvent struct {
Type uint32
Timestamp uint32
}
// WindowEvent (https://wiki.libsdl.org/SDL_WindowEvent)
type WindowEvent struct {
Type uint32
Timestamp uint32
WindowID uint32
Event uint8
_ uint8 // padding
_ uint8 // padding
_ uint8 // padding
Data1 int32
Data2 int32
}
As you can see, all of the other Events have the field Type. How can I access this?
Solution
This how I ended up polling events in this SDL2 binding for Go, in case anyone is wondering:
func PollEvents() {
for {
if event := sdl.PollEvent(); event != nil {
switch event.(type) {
case *sdl.QuitEvent:
utils.Running = false
}
} else {
break
}
}
}
You actually can't. Interfaces only define a method set that is available on a type, they do nothing to expose fields. In your case I would recommend doing a type switch. It would look a little like this;
switch v := myInstance.(type) {
case CEvent:
fmt.Println(v)
case CommonEvent:
fmt.Println(v)
case WindowEvent:
fmt.Println(v)
default:
fmt.Println("unknown")
}
You may want to structure your code a bit differently depending on what you're doing with the instance after this but that gives you the basic idea. You can also do a type assertion with a single type like; v, err := myInstance.(CommonEvent) but I doubt it would be as effective here. It also returns an error if the type of myInstance is not CommonEvent so it's not really the best way to go about figuring out what type and interface instance may be.
You will need to know the type. Let's say we know it's a CEvent:
cEvent, ok := Event.(CEvent)
if !ok {
// You lied, not a CEvent
return
}
// Otherwise, you can get the type!
fmt.Println(cEvent.Type)
Of course if you don't know the type, you can keep type asserting until you get it right. Otherwise, throw an error, return a default value, etc:
func getType(i interface{}) uint32 {
cEvent, ok := i.(CEvent)
if ok {
return cEvent.Type
}
commonEvent, ok := i.(CommonEvent)
if ok {
return commonEvent.Type
}
// Etc
return <default>
}
You can spend a lot of time doing reflection calls or trying to guess the type or using type switches.
Or you can just define an interface with functions that return the information you need.
For example you could do
type Event interface {
GetCommonEvent() *CommonEvent
GetWindowEvent() *WindowEvent
}

Check if Flag Was Provided in Go

With the flag package, is there a good way to distinguish if a string flag was passed?
For example, when the flag is not passed, I want to set it to a dynamic default value. However, I want to set it to empty if the flag was provided but with a value of "".
Current I am doing the following:
flagHost = flag.String(flagHostFlagKey, "", "...")
...
setHostname := false
for _, arg := range os.Args {
if arg == "-"+flagHostFlagKey {
setHostname = true
}
}
if !setHostname {
...
Which seems to work fine, but is kind of ugly. Is there a better way while staying with the standard flag package?
Use the flag.Visit()
Description:
Visit visits the command-line flags in lexicographical order, calling fn for each. It visits only those flags that have been set.
use:
func isFlagPassed(name string) bool {
found := false
flag.Visit(func(f *flag.Flag) {
if f.Name == name {
found = true
}
})
return found
}
The built-in flag types don't support distinguishing default values and explicit assignment to the default value. However, the flag package is quite flexible, and allows you to roll your own type that does, using the flag.Value interface.
Here's a full example that contains a string flag which records if it's been assigned to.
package main
import (
"flag"
"fmt"
)
type stringFlag struct {
set bool
value string
}
func (sf *stringFlag) Set(x string) error {
sf.value = x
sf.set = true
return nil
}
func (sf *stringFlag) String() string {
return sf.value
}
var filename stringFlag
func init() {
flag.Var(&filename, "filename", "the filename")
}
func main() {
flag.Parse()
if !filename.set {
fmt.Println("--filename not set")
} else {
fmt.Printf("--filename set to %q\n", filename.value)
}
}
Here's some example runs:
$ go run a.go -filename=abc
--filename set to "abc"
$ go run a.go -filename=
--filename set to ""
$ go run a.go
--filename not set
The issue with using a custom flag type (the stringFlag example in this thread) is you'll slightly upset the PrintDefaults output (i.e. --help). For example, with a string username flag and a stringFlag servername flag, --help looks like this:
-server value
server:port (default localhost:1234)
-username string
username (default "kimmi")
Note these are both string arguments as far as the user is concerned, but presented differently as a stringFlag is not a string.
flag's Flagset has an internal map that includes the flags that were declared ('formal') and those actually set ('actual'). The former is available via Lookup(), though alas the latter is not exposed, or you could just write:
var servername = flag.String("server", "localhost:8129", "server:port")
flag.Parse()
if f := flag.CommandLine.LookupActual("server"); f != nil {
fmt.Printf("server set to %#v\n", f)
} else {
fmt.Printf("server not set\n")
}
Seems like the best you can do, if you want consistent PrintDefaults() output, is to use Visit to extract your own view of 'actual' (VisitAll does the same thing with 'formal'):
var servername = flag.String("server", "localhost:8129", "server:port")
flag.Parse()
flagset := make(map[string]bool)
flag.Visit(func(f *flag.Flag) { flagset[f.Name]=true } )
if flagset["server"] {
fmt.Printf("server set via flags\n")
} else {
fmt.Printf("server not explicitly set, using default\n")
}
To use a dynamic default value for a flag, create the flag with the default set to the dynamic value:
func main() {
flagHost = flag.String(flagHostFlagKey, computedHostFlag(), "...")
flag.Parse()
// *flagHost equals the return value from computedHostFlag() if
// the flag is not specified on the command line.
...
}
With this approach, it's not necessary to detect if the flag is specified on the command line. Also, help shows the correct default.
If the computed value depends on other flags or is expensive to calculate, then use the approach suggested in one of the other answers.
Face with same problem, but have even complex case with bool flag, in this case computedHostFlag() not working, since you can provide to flag creation only true or false. "type stringFlag struct" solution also not the best, since ruin idea of default values.
Solve it in this way: create two sets of flags, with different default values, after parse - just check - if flag in first flagset have the same value that flag from second flagset - that it means that flag value was provided by user from command line. If they different - than this mean that flag was set by default.
package main
import (
"fmt"
"flag"
)
func main() {
args := []string{"-foo="}
flagSet1 := flag.NewFlagSet("flagSet1", flag.ContinueOnError)
foo1 := flagSet1.String("foo", "-", ``)
boolFoo1 := flagSet1.Bool("boolfoo", false, ``)
flagSet1.Parse(args)
flagSet2 := flag.NewFlagSet("flagSet2", flag.ContinueOnError)
foo2 := flagSet2.String("foo", "+", ``)
boolFoo2 := flagSet2.Bool("boolfoo", true, ``)
flagSet2.Parse(args)
if *foo1 != *foo2 {
fmt.Println("foo flag set by default")
} else {
fmt.Println("foo flag provided by user")
}
if *boolFoo1 != *boolFoo2 {
fmt.Println("boolfoo flag set by default")
} else {
fmt.Println("boolfoo flag provided by user")
}
}
playground: https://play.golang.org/p/BVceE_pN5PO , for real CLI execution, you can do something like that: https://play.golang.org/p/WNvDaaPj585
Same as https://stackoverflow.com/a/35809400/3567989 but with a pointer to a string instead of a custom struct. The *string is nil if unset, non-nil if set.
package main
import (
"flag"
"fmt"
)
type stringPtrFlag struct {
ptr **string
}
func (f stringPtrFlag) String() string {
if *f.ptr == nil {
return ""
}
return **f.ptr
}
func (f stringPtrFlag) Set(s string) error {
*f.ptr = &s
return nil
}
var filename *string
func init() {
flag.Var(stringPtrFlag{&filename}, "filename", "the filename")
}
func main() {
flag.Parse()
if filename == nil {
fmt.Println("--filename not set")
} else {
fmt.Printf("--filename set to %q\n", *filename)
}
}
I think a more reliable way is to check whether any flag in the command-line parameters (os.Args[1:]) is prefixed by "prefix" + str, so the function:
func isInSlice(str string, list []string, prefix string) bool {
for _, v := range list {
if strings.HasPrefix(v, prefix + str) {
return true
}
}
return false
}
I found that we have the Lookup() method:
func isFlagPassed(name string) bool {
rs := flag.Lookup(name)
return rs != nil
}
Full docs
The FlagSet does not have a function LookupActual() in my environment (go version go1.13.4 windows/amd64), and the internal map actual mentioned in Ben L's answer can not be accessed directly.
I have an approach to check if a flag is set using reflect:
import "reflect"
fs := flag.NewFlagSet("the flags", flag.ExitOnError)
flag_name := "host"
host := fs.String(flag_name, "localhost", "specify the host address")
// other flags
fs.Parse(os.Args[1:])
if reflect.Indirect(reflect.ValueOf(fs)).FieldByName("actual").MapIndex(reflect.ValueOf(flag_name)).IsValid() {
fmt.Printf("the host flag is set with value %v", *host)
} else {
fmt.Printf("the host flag is not set")
}

How to initialize struct fields

How can to initialize any fields in golang types? For example:
type MyType struct {
Field string = "default"
}
You can't have "default" values like that, you can either create a default "constructor" function that will return the defaults or simply assume that an empty / zero value is the "default".
type MyType struct {
Field string
}
func New(fld string) *MyType {
return &MyType{Field: fld}
}
func Default() *MyType {
return &MyType{Field: "default"}
}
Also I highly recommend going through Effective Go.
There is no way to do that directly. The common pattern is to provide a New method that initializes your fields:
func NewMyType() *MyType {
myType := &MyType{}
myType.Field = "default"
return myType
// If no special logic is needed
// return &myType{"default"}
}
Alternatively, you can return a non-pointer type. Finally, if you can work it out you should make the zero values of your struct sensible defaults so that no special constructor is needed.

Resources