Reference a boolean for assignment in a struct [duplicate] - go

This question already has answers here:
How to set bool pointer to true in struct literal?
(5 answers)
Closed 11 months ago.
type MyStruct struct {
IsEnabled *bool
}
How do I change value of *IsEnabled = true
None of these work:
*(MyStruct.IsEnabled) = true
*MyStruct.IsEnabled = true
MyStruct.*IsEnabled = true

You can do this by storing true in a memory location and then accessing it as seen here:
type MyStruct struct {
IsEnabled *bool
}
func main() {
fmt.Println("Hello, playground")
t := true // Save "true" in memory
m := MyStruct{&t} // Reference the location of "true"
fmt.Println(*m.IsEnabled) // Prints: true
}
From the docs:
Named instances of the boolean, numeric, and string types are
predeclared. Composite types—array, struct, pointer, function,
interface, slice, map, and channel types—may be constructed using type
literals.
Since boolean values are predeclared, you can't create them via a composite literal (they're not composite types). The type bool has two const values true and false. This rules out the creation of a literal boolean in this manner: b := &bool{true} or similar.
It should be noted that setting a *bool to false is quite a bit easier as new() will initialize a bool to that value. Thus:
m.IsEnabled = new(bool)
fmt.Println(*m.IsEnabled) // False

Related

Unable to assign untyped int to generic struct field [duplicate]

This question already has answers here:
Assigning a value literal to a struct field of a generic type without running into an IncompatibleAssign error
(1 answer)
Convert a type (int, float etc) to `T` [go1.18]
(2 answers)
How to declare and use a struct field which can store both string and int values?
(4 answers)
Closed 4 months ago.
Given a generic struct:
type R2[IDTYPE comparable] struct {
ID IDTYPE
IsActive bool
}
Implementing an interface:
type Storable interface {
Store(ctx context.Context) error
}
I would expect the following definition to work:
func (r R2[int]) Store(ctx context.Context) error {
r.ID = 123 // not allowed
// ...
return nil
}
However, the method definition is not allowed. The error is:
'123' (type untyped int) cannot be represented by the type IDTYPE (int)
Is it not yet possible to do this kind of generic field assignment in Go?
Addendum:
On go playground the error is:
cannot use 123 (untyped int constant) as int value in assignment
And converting to int(123) does not work. The error in this case is:
cannot use comparable(123) (untyped int constant 123) as int value in assignment
Instantiation must happen at the type level, not on a method level, and methods can't introduce new type parameters, see How to create generic method in Go? (method must have no type parameters)
This means when you want to use R2, you then have to choose type arguments for the type parameters, and the methods can't change those, you're "stuck" with the types you choose on R2's instantiation.
Also note that since the constraint for IDTYPE is comparable, which may be string for example, the integer 123 cannot be assigned to the ID field in all cases because it may have a type of string.
If you want / must handle multiple concrete types for the IDs, generics is not the right choice. Interfaces may be used instead:
type R2 struct {
ID any
IsActive bool
}
Also note that the receiver must be a pointer if you wish to modify the receiver (e.g. fields of a struct).
If you wish to restrict the values stored in ID to comparable, use a (generic) function for it.
Here's how you can do it:
type R2 struct {
ID any
IsActive bool
}
func (r *R2) Store(ctx context.Context) error {
setID(r, 123)
return nil
}
func setID[ID comparable](r *R2, id ID) {
r.ID = id
}
Testing it:
r := &R2{}
var s Storable = r
s.Store(context.TODO())
fmt.Println(r)
Which outputs (try it on the Go Playground):
&{123 false}
This provides flexibility (you can set any comparable values to the ID field using setID()), and provides compile-time safety: attempting to set an incomparable value will result in a compile-time error such as this:
setID(r, []int{1}) // Error: []int does not implement comparable

What is the default value of a map of struct

What is the default value of struct in a map? How to check the map value is initialized?
type someStruct struct {
field1 int
field2 string
}
var mapping map[int]someStruct
func main() {
mapping := make(map[int]someStruct)
}
func check(key int) {
if mapping[key] == ? {}
}
Should I check against nil or someStruct{}?
Default value of a struct is zero value for each field which is different on basis of its type.
When storage is allocated for a variable, either through a
declaration or a call of new, or when a new value is created, either
through a composite literal or a call of make, and no explicit
initialization is provided, the variable or value is given a default
value. Each element of such a variable or value is set to the zero
value for its type: false for booleans, 0 for numeric types, "" for
strings, and nil for pointers, functions, interfaces, slices,
channels, and maps. This initialization is done recursively, so for
instance each element of an array of structs will have its fields
zeroed if no value is specified.
type T struct { i int; f float64; next *T }
t := new(T)
the following holds:
t.i == 0
t.f == 0.0
t.next == nil
But for checking the value of a map based on the key if it exists you can use it as:
i, ok := m["route"]
In this statement, the first value (i) is assigned the value stored under the key "route". If that key doesn't exist, i is the value type's zero value (0). The second value (ok) is a bool that is true if the key exists in the map, and false if not.
For your question
Should I check against nil or someStruct{} ?
To check for initialized empty struct you can check for someStruct{} as:
package main
import (
"fmt"
)
type someStruct struct {
field1 int
field2 string
}
var mapping map[int]someStruct
func main() {
var some someStruct
fmt.Println(some == (someStruct{}))
//mapping := make(map[int]someStruct)
}
Go playground

Detecting uninitialized struct fields

I have a service, written in golang, that can be initialized with some options. Options are passed as a struct to a constructor function. The constructor function uses default values for option fields that weren't included. Here's an example:
type Service struct {
options Options
}
type Options struct {
BoolOption bool
StringOption string
}
const (
DefaultBoolOption = true
DefaultStringOption = "bar"
)
func New(opts Options) *Service {
if !opts.BoolOption {
opts.BoolOption = DefaultBoolOption
}
if len(opts.StringOption) < 1 {
opts.StringOption = DefaultStringOption
}
return &Service{options: opts,}
}
...
// elsewhere in my code
o := Options{BoolOption: false,}
// sets s.options.BoolOption to true!!!
s := New(o)
My problem is that for bool or int option types, I can't tell whether or not a field value was set in the Options struct. For example, was BoolOption set to false or just not initialized?
One possible solution would be to use strings for all of my Options fields. For instance, BoolOption would be "false" rather than false. I could then check the length of the string to check if it was initialized.
Are there better solutions?
For detecting uninitialized struct fields, as a rule certain types have zero values, they are otherwise nil (maps and channels need to be maked):
var i int // i = 0
var f float64 // f = 0
var b bool // b = false
var s string // s = ""
var m chan int// c = nil
There is a tour slide which is useful.
I think you're problem involves creating a default boolean value of true
For your sake, however, if you are passing an Option struct into that function, and the user did not specify the options, then yes they will be false and "" by default.
One solution is to create a(nother) constructor: func NewOptions(b bool, s string) Options
This constructor would make the bool default true.
You could also try an enum to simulate a boolean; the zero value will be 0. So instead of type bool for the Options struct value BoolOption, you could use int:
const (
TRUE
FALSE
)
You could also have a UNINITIALIZED as zero, but I think you just want to switch the default value, rather than detecting that it is uninitialized.
So when checking a value of Options, you could write:
if opt.BoolOption == FALSE {
// Do something with the option
} else {
// The default option
}
A bit simpler would be to make the BoolOption into notBoolOption, and have the default be false.
Or, as Olivia Ruth points out, if you have the field as a pointer to a boolean, it will be remain nil until "initialized".
The default value for a bool is false. If you notice that you are overwriting the options value to true every time you run the code.
The default values of a strings variable is "". So if check the string with len(opts.StringOption) == 0 or opts.StringOption == "" that would work as well.
https://play.golang.org/p/jifZZvoBVZ

Identify non builtin-types using reflect

I need to differentiate such types as
type A []byte
from a []byte. Using reflect, reflect.TypeOf(A{}).Kind tells me that it is a Slice of byte. How can I differentiate []byte{} from A{}, without having a bounded list of types to check for?
Are there new ways to do it in newer versions of Go?
Some background
First let's clear some things related to types. Quoting from Spec: Types:
A type determines the set of values and operations specific to values of that type. Types may be named or unnamed. Named types are specified by a (possibly qualified) type name; unnamed types are specified using a type literal, which composes a new type from existing types.
So there are (predeclared) named types such as string, int etc, and you may also create new named types using type declarations (which involves the type keyword) such as type MyInt int. And there are unnamed types which are the result of a type literal (applied to / including named or unnamed types) such as []int, struct{i int}, *int etc.
You can get the name of a named type using the Type.Name() method, which "returns an empty string for unnamed types":
var i int = 2
fmt.Printf("%q\n", reflect.TypeOf("abc").Name()) // Named: "string"
fmt.Printf("%q\n", reflect.TypeOf(int(2)).Name()) // Named: "int"
fmt.Printf("%q\n", reflect.TypeOf([]int{}).Name()) // Unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(struct{ i int }{}).Name()) // Unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(&struct{ i int }{}).Name()) // Unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(&i).Name()) // Unnamed: ""
There are types which are predeclared and are ready for you to use them (either as-is, or in type literals):
Named instances of the boolean, numeric, and string types are predeclared. Composite types—array, struct, pointer, function, interface, slice, map, and channel types—may be constructed using type literals.
Predeclared types are:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
You may use Type.PkgPath() to get a named type's package path, which "if the type was predeclared (string, error) or unnamed (*T, struct{}, []int), the package path will be the empty string":
fmt.Printf("%q\n", reflect.TypeOf("abc").PkgPath()) // Predeclared: ""
fmt.Printf("%q\n", reflect.TypeOf(A{}).PkgPath()) // Named: "main"
fmt.Printf("%q\n", reflect.TypeOf([]byte{}).PkgPath()) // Unnamed: ""
So you have 2 tools available to you: Type.Name() to tell if the type is a named type, and Type.PkgPath() to tell if the type is not predeclared and is a named type.
But care must be taken. If you use your own, named type in a type literal to construct a new type (e.g. []A), that will be an unnamed type (if you don't use the type keyword to construct a new, named type):
type ASlice []A
fmt.Printf("%q\n", reflect.TypeOf([]A{}).PkgPath()) // Also unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(ASlice{}).PkgPath()) // Named: "main"
What can you do in such cases? You may use Type.Elem() to get the type's element type, if type's Kind is Array, Chan, Map, Ptr, or Slice (else Type.Elem() panics):
fmt.Printf("%q\n", reflect.TypeOf([]A{}).Elem().Name()) // Element type: "A"
fmt.Printf("%q\n", reflect.TypeOf([]A{}).Elem().PkgPath()) // Which is named, so: "main"
Summary
Type.PkgPath() can be used to "filter out" predeclared and unnamed types. If PkgPath() returns a non-empty string, you can be sure it's a "custom" type. If it returns an empty string, it still may be an unnamed type (in which case Type.Name() returns "") constructed from a "custom" type; for that you may use Type.Elem() to see if it is constructed from a "custom" type, which may have to be applied recursively:
// [][]A -> Elem() -> []A which is still unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf([][]A{}).Elem().PkgPath())
// [][]A -> Elem() -> []A -> Elem() -> A which is named: "main"
fmt.Printf("%q\n", reflect.TypeOf([][]A{}).Elem().Elem().PkgPath())
Try all the examples on the Go Playground.
Special case #1: Anonymous struct types
There is also the case of an anonymous struct type which is unnamed, but it may have a field of a "custom" type. This case can be handled by iterating over the fields of the struct type and performing the same check on each field, and if any of them is found to be a "custom" type, we can claim the whole struct type to be "custom".
Special case #2: Map types
In case of maps we may consider an unnamed map type "custom" if any of its key or value type is "custom".
The value type of a map can be queried with the above mentioned Type.Elem() method, and the key type of a map can be queried with the Type.Key() method - we also have to check this in case of maps.
Example implementation
func isCustom(t reflect.Type) bool {
if t.PkgPath() != "" {
return true
}
if k := t.Kind(); k == reflect.Array || k == reflect.Chan || k == reflect.Map ||
k == reflect.Ptr || k == reflect.Slice {
return isCustom(t.Elem()) || k == reflect.Map && isCustom(t.Key())
} else if k == reflect.Struct {
for i := t.NumField() - 1; i >= 0; i-- {
if isCustom(t.Field(i).Type) {
return true
}
}
}
return false
}
Testing it (try it on the Go Playground):
type K int
var i int = 2
fmt.Println(isCustom(reflect.TypeOf(""))) // false
fmt.Println(isCustom(reflect.TypeOf(int(2)))) // false
fmt.Println(isCustom(reflect.TypeOf([]int{}))) // false
fmt.Println(isCustom(reflect.TypeOf(struct{ i int }{}))) // false
fmt.Println(isCustom(reflect.TypeOf(&i))) // false
fmt.Println(isCustom(reflect.TypeOf(map[string]int{}))) // false
fmt.Println(isCustom(reflect.TypeOf(A{}))) // true
fmt.Println(isCustom(reflect.TypeOf(&A{}))) // true
fmt.Println(isCustom(reflect.TypeOf([]A{}))) // true
fmt.Println(isCustom(reflect.TypeOf([][]A{}))) // true
fmt.Println(isCustom(reflect.TypeOf(struct{ a A }{}))) // true
fmt.Println(isCustom(reflect.TypeOf(map[K]int{}))) // true
fmt.Println(isCustom(reflect.TypeOf(map[string]K{}))) // true

Checking types of slices in golang

I use the reflect package to check the type of my variables. For example if I want to check if var is an integer I do:
reflect.TypeOf(var).Kind == reflect.Int
How can I check if my variable is an int or float slice?
I can only see Slice as one of the types returned by Kind() but this slice could be of any type
If a type is slice,Elem() will return the underlying type:
func main() {
foo := []int{1,2,3}
fmt.Println(reflect.TypeOf(foo).Elem()) //prints "int"
fmt.Println(reflect.TypeOf(foo).Elem().Kind() == reflect.Int) //true!
}
You better check that it's a slice before, of course.

Resources