Detecting uninitialized struct fields - go

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

Related

How to get a Protobuf enum value from its string representation?

I can get the string value of a Protobuf enum with this instruction:
str := testPB.Status_ENABLED.String()
How can I perform the inverse operation? (from a string, get the enum element).
The generated code has a map called <EnumName>_value of type map[string]int32. Then you can convert the numerical value to the actual defined type:
num := testPB.Status_value[str]
v := testPB.Status(num)
Consider that if the str value doesn't exist in the map (note that it's case sensitive), the map look-up will return 0. Depending on how you defined your protobuffer, the 0 value might be mapped to a an enum instance that does not have "zero" semantics. This is why it is recommended to map 0 to an "unknown" instance:
enum Status {
UNKNOWN = 0;
ENABLED = 1;
// and so on
}
Which in Go correctly yields a makeshift zero-value if the string representation is effectively unknown:
v := testPB.Status(testPB.Status_value["does_not_exist"])
fmt.Println(v == testPB.Status_UNKNOWN) // true
Go 1.18
With generics, it is possible to write reusable code to construct protobuffer enums from string values:
func Enum[T ~string, PB ~int32](val T, pbmap map[string]int32, dft PB) PB {
v, ok := pbmap[string(val)]
if !ok {
return dft
}
return PB(v)
}
where:
the type parameter T is the string representation, which could also be a type with underlying type string, e.g. type MyEnum string
the argument pbmap is the <EnumName>_value from the generated protobuffer code
the type parameter PB is the protobuffer enum type.
The function above takes a default value of type PB to (obviously) have a fallback in case the string representation is invalid, and also to allow type inference on PB, which otherwise would be used only as a return type.
Usage:
type SomeEnum string
const (
SomeEnumFoo SomeEnum = "FOO"
SomeEnumBar SomeEnum = "BAR"
)
func main() {
foo := SomeEnumFoo
v := Enum(foo, pb.SomeEnum_value, pb.SomeEnum_Foo)
// ^ T ^ map[string]int32 ^ default PB value
// v is type pb.SomeEnum
}

Check if struct field is empty

I would like to iterate over the fields of a struct after unmarshalling a JSON object into it and check for the fields whose value was not set (i.e. are empty).
I can get the value of each field and compare that to the reflect.Zero value for the corresponding type
json.Unmarshal([]byte(str), &res)
s := reflect.ValueOf(&res).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
v := reflect.ValueOf(f.Interface())
if (reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())) {
....
But the problem, of course, is that this will not work well for bool or int values.
If a bool field is set to false in the JSON or an int field is set to 0, they will be equal to the zero value of their type. The aforementioned check will consider the fields to be uninitialized, even though they actually have a value set.
I know one way around this is to use pointers, but I just don't see how that would be possible in this case as I'm working with reflect.Value types, not the actual struct.
As you've mentioned, you could use pointers.
The json package can handle unmarshalling values into pointers for you. You've not included the json payload you are trying to unmarshal, or the struct you are unmarshalling into, so I've made up an example.
// json
{
"foo": true,
"number_of_foos": 14
}
// go struct
type Foo struct {
Present bool `json:"foo"`
Num int `json:"number_of_foos"`
}
Here if the keys foo or number_of_foos is missing, then as you've correctly observed, the zero value (false/ 0) will be used. In general the best advice is to make use of the zero value. Create structures so that zero values of false are useful, rather than a pain.
This is not always possible, so changing the types of the fields in the Foo struct to be pointers will allow you to check the 3 cases you are after.
Present
Present and zero
Missing
here is the same struct with pointers:
// go struct
type Foo struct {
Present *bool `json:"foo"`
Num *int `json:"number_of_foos"`
}
Now you can check for presence of the value with fooStruct.Present != nil and if that condition holds, you can assume that the value in the field is the one you wanted.
There is no need to use the reflect package.
Another way of doing the same is by implementing json.Unmarshaler.
type MaybeInt struct {
Present bool
Value int
}
func (i *MaybeInt) UnmarshalJSON(bs []byte) error {
if e := json.Unmarshal(bs, &i.Value); e != nil {
return e
}
i.Present = true
return nil
}
You can then use MaybeInt in your top-level structure:
type Top struct {
N MaybeInt `json:"n"`
M MaybeInt `json:"m"`
}
func main() {
t := Top{}
if e := json.Unmarshal([]byte(` { "n": 4930 } `), &t); e != nil {
panic(e)
}
fmt.Println(t.N, t.M)
}
See it working on the playground
Try using the golang validator package. The validator package offers a required attribute that might do the required job for your need. The official documentation for required attribute states:
This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil.
The example illustrating the same can be seen at: https://github.com/go-playground/validator/blob/v9/_examples/struct-level/main.go.
Hope this solves your requirement.

Check if every item in a struct is unchanged

I have the following package:
// Contains state read in from the command line
type State struct {
Domain string // Domain to check for
DomainList string // File location for a list of domains
OutputNormal string // File to output in normal format
OutputDomains string // File to output domains only to
Verbose bool // Verbose prints, incl. Debug information
Threads int // Number of threads to use
NoColour bool // Strip colour from output
Silent bool // Output domains only
Usage bool // Print usage information
}
func InitState() (state State) {
return State { "", "", "", "", false, 20, false, false, false }
}
func ValidateState(s *State) (result bool, error string ) {
if s.Domain == "" && s.DomainList == "" {
return false, "You must specify either a domain or list of domains to test"
}
return true, ""
}
Within ValidateState() I would like to return true if every item in State is the same as what is defined in InitState(). I can see a few ways to do this, but nothing that seems concise. I would greatly value some direction!
Struct values are comparable if all their fields are comparable (see Spec: Comparison operators). And since in your case this holds, we can take advantage of this.
In your case the simplest and most efficient way to achieve this is to save a struct value holding the initial value, and whenever you want to tell if a struct value (if any of its fields) has changed, simply compare it to the saved, initial value. This is all it takes:
var defaultState = InitState()
func isUnchanged(s State) bool {
return s == defaultState
}
Testing it:
s := InitState()
fmt.Println(isUnchanged(s))
s.Threads = 1
fmt.Println(isUnchanged(s))
Output (try it on the Go Playground):
true
false
Note that this solution will still work without any modification if you change the State type by adding / removing / renaming / rearranging fields as long as they all will still be comparable. As a counter example, if you add a field of slice type, it won't work anymore as slices are not comparable. It will result in a compile-time error. To handle such cases, reflect.DeepEqual() might be used instead of the simple == comparison operator.
Also note that you should create default values of State like this:
func NewState() State {
return State{Threads: 20}
}
You don't have to list fields whose values are the zero values of their types.

How to resolve whether pass objects via interface{} have not initializated fields

I have problem with resolve whether object which was pass as interface to function hasn't initializated fields, like object which was defined as just someObject{} is a empty, because all fields, has value 0, or nil
Problem becomes more complicated if I pass diffrent objects, because each object have diffrent type field value so on this moment I don't find universal way to this.
Example
func main(){
oo := objectOne{}
ot := objectTwo{}
oth := objectThree{"blah" , "balbal" , "blaal"}
resolveIsNotIntialized(oo)
resolveIsNotIntialized(ot)
resolveIsNotIntialized(oth)
}
func resolveIsNotIntialized(v interface{}) bool{
// and below, how resolve that oo and ot is empty
if (v.SomeMethodWhichCanResolveThatAllFiledIsNotIntialized){
return true
}
return false
}
I want to avoid usage switch statement like below, and additional function for each object, ofcorse if is possible.
func unsmartMethod(v interface{}) bool{
switch v.(type){
case objectOne:
if v == (objectOne{}) {
return true
}
// and next object, and next....
}
return false
}
As Franck notes, this is likely a bad idea. Every value is always initialized in Go. Your actual question is whether the type equals its Zero value. Generally the Zero value should be designed such that it is valid. The better approach would generally be to create an interface along the lines of:
type ZeroChecker interface {
IsZero() bool
}
And then attach that to whatever types you want to check. (Or possibly better: create an IsValid() test instead rather than doing your logic backwards.)
That said, it is possible to check this with reflection, by comparing it to its Zero.
func resolveIsNotIntialized(v interface{}) bool {
t := reflect.TypeOf(v)
z := reflect.Zero(t).Interface()
return reflect.DeepEqual(v, z)
}
(You might be able to get away with return v == z here; I haven't thought through all the possible cases.)
I don’t think there is a good reason (in idiomatic Go) to do what you are trying to do. You need to design your structs so that default values (nil, empty string, 0, false, etc.) are valid and represent the initial state of your object. Look at the source of the standard library, there are lots of examples of that.
What you are suggesting is easily doable via Reflection but it will be slow and clunky.
You could narrow the type which your function takes as an argement a little, not take an interface{} but accept one that allows you to check for non-zero values, say type intercae{nonZero() bool} as in the example code below. This will not tell you explicitly that it hasn't been set to the zero value, but that it is not zero.
type nonZeroed interface {
nonZero() bool
}
type zero struct {
hasVals bool
}
func (z zero) nonZero() bool {
return z.hasVals
}
type nonZero struct {
val int
}
func (nz nonZero) nonZero() bool {
return nz.val != 0
}
type alsoZero float64
func (az alsoZero) nonZero() bool {
return az != 0.0
}
func main() {
z := zero{}
nz := nonZero{
val: 1,
}
var az alsoZero
fmt.Println("z has values:", initialized(z))
fmt.Println("nz has values:", initialized(nz))
fmt.Println("az has values:", initialized(az))
}
func initialized(a nonZeroed) bool {
return a.nonZero()
}
Obviously as the type get more complex additional verification would need to be made that it was "nonZero". This type of pattern could be used to check any sort condition.

Reference a boolean for assignment in a struct [duplicate]

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

Resources