If I'm passing an int value in a struct (in my particular case, rpc arguments), the language does not allow the attribute to be nil. The empty value for an int is 0.
But Go uses 0-indexed arrays. I need a way to differentiate between an empty value and an index of 0. Is there an idiomatic go solution for this problem?
// this is psuedo-code I had written before hitting this problem
if (args.maybeIndex != nil) {
doSomething(sliceOfNodes[args.maybeIndex])
}
If you encode your ints by value, then there's not much you can do about it - the default value is 0.
A common way to ensure nullability in encodings in Go is to use pointer types. Using a *int instead of an int lets you distinguish between "none" and 0.
E.g. with a JSON example, consider the struct:
type Options struct {
Id *string `json:"id,omitempty"`
Verbose *bool `json:"verbose,omitempty"`
Level *int `json:"level,omitempty"`
Power *int `json:"power,omitempty"`
}
And this data:
{
"id": "foobar",
"verbose": false,
"level": 10
}
Note that "power" is not specified. You could write a deserializer:
func parseOptions(jsn []byte) Options {
var opts Options
if err := json.Unmarshal(jsonText, &opts); err != nil {
log.Fatal(err)
}
if opts.Power == nil {
var v int = 10
opts.Power = &v
}
return opts
}
That sets a default value to "power", if it's not specified. This lets you distinguish between "power was not present" and "power was present and its value was 0".
If your encoding / RPC mechanism does not permit pointers, you could work around this by having another boolean field called "index present" or something like this.
Finally, consider designing your program to make it resilient to the difference between "not set" and "set to default value". IOW, just accept that default values and unspecified data are one and the same. In the long term, this will result in a cleaner design and code, and will be less error prone.
Related
This question already has answers here:
preserve int64 values when parsing json in Go
(3 answers)
Closed 1 year ago.
I’m working on a rest api but for some reason can't type assert an interface{} to its underlying type - int.
I send data via post request, to create an ad. It looks like so:
POST http://localhost:8080/api/ads
{
"Title": "Example Title",
"Section": "machinery",
"CostPerDayInCent": 34500,
"Description": "A description",
"User": 4,
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
}
The passed values are decoded into a map[string]interface{} like so:
var adToValidate map[string]interface{}
err = json.NewDecoder(r.Body).Decode(&adToValidate)
if err != nil {
api.errorLog.Printf("Error decoding ad object: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
My problem happens when I type assert the costperdayincent and user interface values to int.
localScopeAd.CostPerDayInCent, ok = adMapToValidate[“CostPerDayInCent”].(int)
if !ok {
fieldErrors[structFieldName] = fmt.Sprintf("%v value is not of int type", structFieldName)
}
The if statement executes, indicating it cant type assert.
Why is this? Is it because the passed json treats every value as a string?
How would I resolve this issue?
FOLLOW UP
I resolved this issue with the help of #DanielFarrell 's answer.
As I couldn't fit a response in the comment section, below is my reason for decoding into a map and not a struct:
I understand it would make a lot more sense to decode into a struct.
I had initially been decoding into a struct. However I had run into some issues when trying to validate the bool values.
i.e
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
If a user was to make a post request to create an ad leaving out the above values. When the request body was decoded the above fields would default to false in the struct (bools 0 value).
If i was then going to validate the passed in values I wouldn't know if the user had entered false or had left out a whole key value pair.
As I wanted to ensure the user had entered these values I first decoded into a map so that I could check if the key of the bool key value pair was present.
Then I could send the relevant response if there was some missing required bool data.
If you know of a simpler way of going about the above I'd be interested in hearing it. There's some 3rd party packages that have 3 value boolean types which may have worked but I decided to go with the above instead.
I would prefer to decode into a struct and let json/encoding do the work of handling proper type.
type Input struct {
Title,
Selection string
CostPerDayInCent int64
Description string
User int64
AllowMobileContact,
AllowEmailContact,
IsActive bool
}
This approach is quite common and useful for several reasons. First, you get to choose the type. Second, you are defining a type, so you get to attach functions, which I find quite convenient as a method to organize my code and avoid having to pass structures as explicit function arguments. Third, you can attach annotations to the struct's fields, further adjusting unmarshalling.
Finally, and to me, most importantly, you're thinking about data the way Go handles it. In many of the popular modern languages such as Python, the code - and the documentation - doesn't usually attach types to functions, or attempt to explicitly enumerate attributes of objects. Go is different. Its removal of inherent language reflection is one of the reasons it is so performant. This can provide vast benefits to the programmer - when you know all the types, you know exactly how things will behave, and can precisely identify what you must pass the functions you call. I suggest you embrace the explicit typing, and avoid decoding json into interface-oriented types whenever possible. You'll know what you're handling, and you'll be able to define it explicitly for your consumers as well.
https://play.golang.org/p/egaequIT8ET contrasts your approach, in which you don't need to bother defining the types - but you also don't have the opportunity to choose them.
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type Input struct {
Title,
Selection string
CostPerDayInCent int64
Description string
User int64
AllowMobileContact,
AllowEmailContact,
IsActive bool
}
func main() {
var input = `{
"Title": "Example Title",
"Section": "machinery",
"CostPerDayInCent": 34500,
"Description": "A description",
"User": 4,
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
}`
// map[string]interface
data := make(map[string]interface{})
if err := json.NewDecoder(bytes.NewBufferString(input)).Decode(&data); err != nil {
panic(err)
}
fmt.Printf("%f %T\n", data["CostPerDayInCent"], data["CostPerDayInCent"])
// structure
obj := new(Input)
if err := json.NewDecoder(bytes.NewBufferString(input)).Decode(obj); err != nil {
panic(err)
}
fmt.Printf("%d %T", obj.CostPerDayInCent, obj.CostPerDayInCent)
}
I am a long time python user moving to Go, and I still have some issues to reacquire basic skill to manage typing and pointer.
I have a program receiving event from RabbitMq (But the problem would be the same no matter what transport we are talking about). One of the even contain an optional field F1 typed as int.
My understanding is, if the field is not present in the event, then go will default it to 0. But 0 is a valid value for that field, and I need to differentiate cases where the value is 0, and cases where the value is non defined.
I thought to make my field a *int to actually have "nil" as a value. But then when when a receive an event, will F1 be set to the actual pointed value, or the value address from the sender?
Do I have any other alternative?
In most cases, using a pointer to the value makes sense. E.g
type RabbitMessage struct {
F1 *int `json:"f1"`
}
The exact details of how this will work depends on how you serialise your data before sending it over RabbitMQ. If you are using JSON, then there should be no issue with this as both a null value, and an omitted value, will be represented in Go as nil. When the value is provided, it will be set to the value you expect (it will not use the address from the sender).
If you control only the receiver program, then AFAICT you can not differentiate between an int that has been automatically initialized to 0 by go from an int that has been set to 0 by the sender.
If you can modify the sender program though, an alternative could be to add a boolean field along with your int, telling whether the int is set or not. Then on the receiving end you can check whether the boolean is true or not.
You can also send a pointer to an int:
type Message struct {
Value *int `json:"value"`
}
message := Message{Value: 4}
Be aware though that when unmarshalling this you'll get an int pointer you'll need to dereference.
"Do I have any other alternative?" -- Yes, you can define a custom type, similar to sql.NullInt64.
type OptionalInt struct {
Int int
IsValid bool
}
func NewOptionalInt(i int) OptionalInt {
return OptionalInt{Int: i, IsValid: true}
}
func (o *OptionalInt) UnmarshalJSON(data []byte) error {
if string(data) != "null" {
if err := json.Unmarshal(data, &o.Int); err != nil {
return err
}
o.IsValid = true
}
return nil
}
func (o OptionalInt) MarshalJSON() ([]byte, error) {
if o.IsValid {
return json.Marshal(o.Int)
}
return json.Marshal(nil)
}
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.
How can I check the nil value as a result of the assignment of variable b below?
package main
import (
"fmt"
"net"
)
type Subnet struct {
ID int
IP *net.IPNet
}
func main() {
var s = Subnet{
ID: 12345,
IP: &net.IPNet{
IP: net.IP{10, 1, 232, 0},
Mask: net.IPMask{255, 255, 255, 0},
},
}
fmt.Printf("%+v\n", s)
var b = Subnet{
ID: 12345,
IP: &net.IPNet{},
}
fmt.Printf("%+v\n", b)
if b.IP == nil {
fmt.Println("hello there")
}
}
Here is the go playground https://play.golang.org/p/Jk6_3ofyH5
Basically, I expect that "hello there" will be printed out as b.IP is nil, but it did not.
It looks like you're confusing the value nil with a concept called "zero value".
The zero value
Any type in Go — no matter whether standard or created by the user —
has a "zero value" which is the value assigned automatically to any variable of that type which was not explicitly initialized otherwise.
IOW, when you have
type T …
var v T
the value of the variable "v" will be the zero value of type T.
The concept of the zero value follows naturally from the Go's property
of not allowing uninitialized variables: variables in Go always have
sensible values (contraty to say, C).
The nil value
A special value, nil, which is a predeclared identifier in Go,
is the zero value for a set of types which have reference semantics.
The term "reference semantics" might be intimidating but it can be
explained pretty simply: a type has reference semantics when variables of
it reference some data structure instead of directly containing it.
Hence when you copy the value of a variable of such type into another
variable, both variables reference the same data structure.
As you should know, maps, slices, interfaces, function values, and pointers have reference semantics in Go, and that's why their zero values are nil.
The distinction
The major takeaway to draw from the above is that nil is the zero value
for some types which have a certain property, but not for all of them.
For example, for
type T struct {
A int
B string
}
the zero value is
T{
A: 0,
B: "",
}
that is, a value of type T having its fields initialized
to the zero values corresponding to the types of those fields:
0 for int and "" for string.
Your particular problem
In your type
type Subnet struct {
ID int
IP *net.IPNet
}
the field "IP" has a pointer type *net.IPNet, which is a pointer
to (or, we may say, an address of) a value of the type net.IPNet.
Hence its zero value is nil.
Now if you were to declare something like
var subnet Subnet
this variable would start its life initialized to the zero value
of its type, and that would mean that its field "IP"
would have the zero value for its type, nil.
In your example, you do:
var b = Subnet{
ID: 12345,
IP: &net.IPNet{},
}
and that means creating a variable "b" initialized to a particular
value defined by the literal placed on the right hand of the
assignment operator =.
The field "IP" in that literal is not initialized to the zero value
of its type; instead, it's initialized to a pointer containing the
address of an anonymous variable wihch has the type net.IPNet
and containing the zero value of that type.
To say this in different words, the result it roughly equivalent
to the following:
var ip net.IPNet
var b = Subnet{
ID: 12345,
IP: &ip,
}
There, the variable "ip" contains the zero value of its type,
and the variable "b.IP" contains a pointer to the variable "ip".
Since that field "IP" points to some real memory location,
its value is obviosly not nil, which makes a value of a type having
refence semantics "point to nowhere",
and that's why the test in your if statement fails.
The check should be like,
if b.IP != nil {
fmt.Println("hello there")
}
And the output will have hello-world,
{ID:12345 IP:10.1.232.0/24}
{ID:12345 IP:}
hello there
Note:Golang's nil is not same as Cpp's NULL, it is the opposite.
nil is a predeclared identifier in Go. For example,
package main
import (
"fmt"
"net"
)
type Subnet struct {
ID int
IP *net.IPNet
}
func main() {
var b = Subnet{ID: 1, IP: nil}
fmt.Println(b.IP == nil)
}
Output:
true
Playground: https://play.golang.org/p/-745-hhLGg
Notice that IPNet has String() string function which makes it implements Stringer interface. Basically this function provides custom string representation of the struct when the struct is printed. If you dig in further to the source code you'll see that this function returns string "<nil>" when either IPNet.IP or IPNet.Mask is nil. This explained how your code prints <nil> when the value of Subnet.IP field is actually not nil.
Now to answer the question in the title literally, you already did the check correctly by comparing the value with nil. In addition, you can try to print using %#v instead, for example :
func main() {
var b = Subnet{ID: 12345, IP: &net.IPNet{}}
fmt.Printf("%#v\n", b)
fmt.Printf("is null: %t\n", b.IP == nil)
var c = Subnet{ID: 12345, IP: nil}
fmt.Printf("%#v\n", c)
fmt.Printf("is null: %t", c.IP == nil)
}
playground
output :
main.Subnet{ID:12345, IP:(*net.IPNet)(0x10444340)}
is null: false
main.Subnet{ID:12345, IP:(*net.IPNet)(nil)}
is null: true
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.