Unmarshal into pointer vs value receiver feilds [duplicate] - go

This question already has answers here:
Difference using pointer in struct fields
(3 answers)
Closed 3 years ago.
I'm learning Golang recently. I know pointer and value receivers how it works generally.
When Unmarshal JSON string like follow 2 examples, I feel that first one (pointer receiver) more efficient way to use memory. But I have seen lots of examples and articles not uses this way. Is there any reason for this? And what are use cases for them?
package main
import (
"encoding/json"
"fmt"
)
type Outer struct {
ID int `json:"id"`
PointerValue *string `json:"pointer_str"`
Inner *Inner `json:"inner"`
}
type Inner struct {
Value string `json:"value"`
}
func main() {
testJson := `{
"id": 1,
"pointer_str": "example-value",
"inner": {
"value": "some-value"
}
}`
testStruct := &Outer{}
json.Unmarshal([]byte(testJson), testStruct)
fmt.Printf("%+v\n", testStruct)
fmt.Printf("%+v\n", *testStruct.PointerValue)
fmt.Printf("%+v\n", testStruct.Inner)
}
Output:
&{ID:1 PointerValue:0x40c250 Inner:0x40c258}
example-value
&{Value:some-value}
Or
package main
import (
"encoding/json"
"fmt"
)
type Outer struct {
ID int `json:"id"`
PointerValue string `json:"pointer_str"`
Inner Inner `json:"inner"`
}
type Inner struct {
Value string `json:"value"`
}
func main() {
testJson := `{
"id": 1,
"pointer_str": "example-value",
"inner": {
"value": "some-value"
}
}`
testStruct := &Outer{}
json.Unmarshal([]byte(testJson), testStruct)
fmt.Printf("%+v\n", testStruct)
fmt.Printf("%+v\n", testStruct.Inner)
}
Output:
&{ID:1 PointerValue:example-value Inner:{Value:some-value}}
{Value:some-value}
Updated: my meaning of efficiency was "efficient way to use memory"

The assumption that it is more efficient is wrong. The one without pointer is more efficient because there is no need for indirection and the value is in the memory cache along with the other values of Outer. Access will be faster.
Use a pointer when the Inner value is optional. It would have a nil value when the value is absent. Otherwise use the form without pointer. You would also use a pointer if the value of Inner in the JSON string may be null.

Related

what's the most efficient way to iterate over an interface{} object in Go? [duplicate]

This question already has answers here:
Iterate through the fields of a struct in Go
(8 answers)
Closed last month.
e.g. Assuming the interface{} object is a struct {"a":1, "b": "test", c: &AnotherStruct{}}, and we need to iterate the object to get value of each field "a", "b", "c".
I can think of two ways:
use Go reflection directly.
use json.Marshal()/json.Unmarshal() to convert the object to map[string]interface{}, and then iterate over the map to do type assertions, this also calls reflection, however there might be some json library having optimizations inside which might gain better performance, e.g. https://github.com/bytedance/sonic.
I was wondering which one is more efficient and is there any other way to do it?
interface{} is (legacy) go short-hand for "this could be anything". It does not represent an "object" (though it could). From go 1.18 onward the keyword any was introduced as a direct replacement for interface{} (though the latter may continue to be used if you need compatibility with older golang versions).
Here-on I shall use any for brevity.
I'd suggest ignoring efficiency unless/until it becomes a problem you need to solve and instead focus on the clearest and simplest way to achieve what you need, functionally.
It is difficult to be clear about what exactly you are faced with and trying to achieve; your "example" object contains both quoted and unquoted field members so I see four possible scenarios that you may be dealing with:
an any variable holding a value that is of a known formal struct type
an any variable holding a value that is of an anonymous formal struct type
an any variable holding a JSON string
an any variable holding a map[string]any
For scenario's 1 and 2 that would be to marshal to JSON then unmarshal to map[string]any.
For scenario 3 you would cast to string then unmarshal to map[string]any:
For scenario 4 you would directly cast to map[string]any.
I have worked up all of these in a playground for you here: https://go.dev/play/p/cSdUmynTFRp
package main
import (
"encoding/json"
"fmt"
)
type AnotherStruct struct {
X int `json:"x"`
}
type Foo struct {
A int `json:"a"`
B string `json:"b"`
C AnotherStruct `json:"c"`
}
func emit(m map[string]any) {
for k, v := range m {
fmt.Printf(" %s: %s\n", k, v)
}
fmt.Println()
}
func scenario1or2(n int, foo any) map[string]any {
fmt.Printf("scenario %d:\n", n)
j, _ := json.Marshal(foo)
m := map[string]any{}
json.Unmarshal(j, &m)
return m
}
func scenario3(foo any) map[string]any {
fmt.Println("scenario 3")
m := map[string]any{}
json.Unmarshal([]byte(foo.(string)), &m)
return m
}
func scenario4(foo any) map[string]any {
fmt.Println("scenario 4")
return foo.(map[string]any)
}
func main() {
emit(scenario1or2(1, Foo{
A: 1,
B: "test",
C: AnotherStruct{X: 42},
}))
emit(scenario1or2(2, struct {
A int
B string
C AnotherStruct
}{
A: 1,
B: "test",
C: AnotherStruct{
X: 42,
},
}))
emit(scenario3(`{"a":1,"b":"test","c":{"x":42}}`))
emit(scenario4(map[string]any{
"a": 1,
"b": "test",
"c": AnotherStruct{
X: 42,
},
}))
}
If you have scenario 1 and simply want efficient access to the fields (i.e. iterating over potentially unknown fields is not actually required) then you can type-cast directly to the known formal struct type:
// assuming...
type Foo struct {
a int
b string
c *AnotherStruct {
}
}
// and where 'anyfoo' is an `any` holding a Foo
foo := anyfoo.(Foo)
a := foo.a
b := foo.b
c := foo.c

Why is the slice field of a struct not appended to? [duplicate]

This question already has answers here:
Assign a new value to a struct field
(2 answers)
Closed 10 months ago.
The output of the following code surprises me:
package main
import (
"fmt"
)
type Thing struct {
mappings map[string]int
orderings []string
}
func NewThing() Thing {
t := Thing{}
t.mappings = make(map[string]int)
return t
}
func (t Thing) Add(s string) {
t.mappings[s] = 1
t.orderings = append(t.orderings, s)
}
func main() {
t := NewThing()
t.Add("foo")
if len(t.mappings) == len(t.orderings) {
fmt.Printf("Equal lengths: %v versus %v", t.mappings, t.orderings)
} else {
fmt.Printf("Unequal lengths: %v versus %v", t.mappings, t.orderings)
}
}
When run on the playground (https://play.golang.org/p/Ph67tHOt2Z_I) the output is this:
Unequal lengths: map[foo:1] versus []
I believe I'm treating the slice correctly; from my understanding it is initialized to nil in NewThing(), and is appended to in Add() (ensuring that the value returned from append is only assigned to its first argument).
Am I missing something incredibly obvious?
I looked at the following resources for an explanation:
https://gobyexample.com/slices - only uses either slice literals (i.e. not a struct field) or slices with set capacities, and I will not know the final size of t.orderings. It's my understanding that append should perform the extension and allocation automatically.
https://go.dev/blog/slices-intro - again, all demonstrations use slice literals. If the fields are moved out of the struct things work as expected. It's only once in the struct that this behavior occurs.
https://yourbasic.org/golang/gotcha-append/ - while it does describe behavior where append does not work as expected, the explanation involves append reusing memory when the slice has enough capacity for a new element, causing unexpected behavior when attempts to append the same array to two different copies. In my case, there is no reassignment of slice operations such as the one in this article, which is discouraged (some_var = append(some_other_var, elem)).
And I looked at the following questions for inspiration:
Go - append to slice in struct: the solution to this question was to assign the result of append back to the field, which I have done.
Correct way to initialize empty slice: the explanation is that slices don't have to be initialized, and can be left as nil and "appended to with allocation", so I believe I'm fine not initializing Thing.orderings.
Incase you don't want to use a pointer ,you can declare a global variable for Thing struct and assign it with the value of t from add function.Here is the code for the same logic :
package main
import (
"fmt"
)
var thing Thing
type Thing struct {
mappings map[string]int
orderings []string
}
func NewThing() Thing {
t := Thing{}
t.mappings = make(map[string]int)
return t
}
func (t Thing) Add(s string) {
t.mappings[s] = 1
t.orderings = append(t.orderings, s)
thing = t
}
func main() {
t := NewThing()
t.Add("foo")
if len(thing.mappings) == len(thing.orderings) {
fmt.Printf("Equal lengths: %v versus %v", thing.mappings, thing.orderings)
} else {
fmt.Printf("Unequal lengths: %v versus %v", thing.mappings, thing.orderings)
}
}
Output:
Equal lengths: map[foo:1] versus [foo]

Common `ToMap` method across structs [duplicate]

This question already has answers here:
Go embedded struct call child method instead parent method
(1 answer)
What is the idiomatic way in Go to create a complex hierarchy of structs?
(1 answer)
Ensuring embedded structs implement interface without introducing ambiguity
(2 answers)
Closed 3 years ago.
We have a few structs and we wanted to have a common ToMap method that converts the struct to Map. It does by marshaling and unmarshalling to json. But it does not seem to work.
Is it because the Mappable struct receives ToMap() and it does not have any member attributes?
If so, is there any way to make ToMap() to be received on Params struct instead of Mappable struct.
I've kept the code below but feel free to edit at https://play.golang.org/p/aDsYxddImxb too
package main
import (
"fmt"
"encoding/json"
)
type Mappable struct {}
func (m *Mappable) ToMap() (reqInterface map[string]interface{}) {
resJson, err := json.Marshal(m)
fmt.Println("**** err ****", err)
json.Unmarshal(resJson, &reqInterface)
return
}
type Params struct {
Mappable
A string `json:"a"`
B string `json:"b"`
C string `json:"c"`
D string `json:"d"`
}
func main() {
params := Params{
A: "dummy",
B: "dummy",
C: "dummy",
D: "dummy",
}
fmt.Printf("struct to convert: %#v \n", params)
fmt.Println("Converted with common ToMap method: ", params.ToMap())
}

Golang: convert struct to embedded at offset 0 struct

I have some different structs like Big with Small embedded at offset 0.
How can I access Small's structure fields from code, that doesn't know anything about Big type, but it is known that Small is at offset 0?
type Small struct {
val int
}
type Big struct {
Small
bigval int
}
var v interface{} = Big{}
// here i only know about 'Small' struct and i know that it is at the begining of variable
v.(Small).val // compile error
It seems that compiler is theoretically able to operate such expression, because it knows that Big type has Small type embedded at offset 0. Is there any way to do such things (maybe with unsafe.Pointer)?
While answer with reflection is working but it has performance penalties and is not idiomatic to Go.
I believe you should use interface. Like this
https://play.golang.org/p/OG1MPHjDlQ
package main
import (
"fmt"
)
type MySmall interface {
SmallVal() int
}
type Small struct {
val int
}
func (v Small) SmallVal() int {
return v.val
}
type Big struct {
Small
bigval int
}
func main() {
var v interface{} = Big{Small{val: 3}, 4}
fmt.Printf("Small val: %v", v.(MySmall).SmallVal())
}
Output:
Small val: 3
Avoid using unsafe whenever possible. The above task can be done using reflection (reflect package):
var v interface{} = Big{Small{1}, 2}
rf := reflect.ValueOf(v)
s := rf.FieldByName("Small").Interface()
fmt.Printf("%#v\n", s)
fmt.Printf("%#v\n", s.(Small).val)
Output (try it on the Go Playground):
main.Small{val:1}
1
Notes:
This works for any field, not just the first one (at "offset 0"). This also works for named fields too, not just for embedded fields. This doesn't work for unexported fields though.
type Small struct {
val int
}
type Big struct {
Small
bigval int
}
func main() {
var v = Big{Small{10},200}
print(v.val)
}

What's the difference between new(Struct) and &Struct{} in Go?

They seem to be the same:
package main
import "fmt"
type S struct {
i int
}
func main() {
var s1 *S = new(S)
fmt.Println(s1)
var s2 *S = &S{}
fmt.Println(s2) // Prints the same thing.
}
Update:
Hm. I just realized that there's no obvious way to initialize S.i using new. Is there a way to do that? new(S{i:1}) does not seem to work :/
From Effective Go:
As a limiting case, if a composite literal contains no fields at all, it creates a zero value for the type. The expressions new(File) and &File{} are equivalent.
Not only do they give the same resulting value, but if we allocate something both ways and look at their values...
// Adapted from http://tour.golang.org/#30
package main
import "fmt"
type Vertex struct {
X, Y int
}
func main() {
v := &Vertex{}
v2 := new(Vertex)
fmt.Printf("%p %p", v, v2)
}
...we'll see that they are in fact allocated in consecutive memory slots. Typical output: 0x10328100 0x10328108. I'm not sure if this is an implementation detail or part of the specification, but it does demonstrate that they're both being allocated from the same pool.
Play around with the code here.
As for initializing with new, according to the language spec: The built-in function new takes a type T and returns a value of type *T. The memory [pointed to] is initialized as described in the section on initial values. Because functions in go can't be overloaded, and this isn't a variadic function, there's no way to pass in any initialization data. Instead, go will initialize it with whatever version of 0 makes sense for the type and any member fields, as appropriate.
Case 1: package main
import (
"fmt"
)
type Drink struct {
Name string
Flavour string
}
func main() {
a := new(Drink)
a.Name = "Maaza"
a.Flavour = "Mango"
b := a
fmt.Println(&a)
fmt.Println(&b)
b.Name = "Frooti"
fmt.Println(a.Name)
}//This will output Frooti for a.Name, even though the addresses for a and b are different.
Case 2:
package main
import (
"fmt"
)
type Drink struct {
Name string
Flavour string
}
func main() {
a := Drink{
Name: "Maaza",
Flavour: "Mango",
}
b := a
fmt.Println(&a)
fmt.Println(&b)
b.Name = "Froti"
fmt.Println(a.Name)
}//This will output Maaza for a.Name. To get Frooti in this case assign b:=&a.

Resources