Function which modifies any structure which has particular fields - go

I have a couple of structures, which inherit some basic structure. Something like this:
type s1 struct {
a string `json:"a"`
b string `json:"b"`
}
type s2 struct {
s1
c string `json:"c"`
d string `json:"d"`
}
type s3 struct {
s1
c string `json:"c"`
d string `json:"d"`
e string `json:"d"`
f string `json:"d"`
}
Now I need to define a function which operates on any structure which has fields a, b. Something like
func modifyStruct(s *s1) {
s.a, s.b = s.b, s.a
}
But has to work on s2, s3 and any other structure which inherits s1. I am trying to achieve this with an interface but so far with no luck. Any way to achieve this? The template is on go-playground.

The general solution is using reflection as shown in Cerise Limón's answer. The cons of using reflection is that you have to export the fields, and that it's slower than it should or could be.
In your example although it is completely fine and sufficient for the function to take a value of type *s1, as all types that embed s1 has a value of s1 (explicitly). The unqualified type name (without package name) acts as the field name for embedded fields:
s2 := s2{s1: s1{"A", "B"}}
fmt.Println(s2)
modifyStruct(&s2.s1)
fmt.Println(s2)
Output (try it on the Go Playground):
{{A B} }
{{B A} }
If you still want it to accept values of "any" (certain) types (so you don't have to refer to the embedded s1 field), but don't want to export the fields, then you may use interfaces for this. Using interfaces you keep the good parts of both solutions (remains fast, flexible and you do not have to export the fields):
type S1 interface {
AB() (string, string)
SetAB(a, b string)
}
type s1 struct {
a string `json:"a"`
b string `json:"b"`
}
func (s s1) AB() (string, string) { return s.a, s.b }
func (s *s1) SetAB(a, b string) { s.a, s.b = a, b }
func modifyStruct(s S1) {
a, b := s.AB()
s.SetAB(b, a)
}
Testing it:
s2 := s2{s1: s1{"A", "B"}}
fmt.Println(s2)
modifyStruct(&s2)
fmt.Println(s2)
Output is the same (try it on the Go Playground):
{{A B} }
{{B A} }
Note that (besides the *s1 type itself) any struct type (and also any pointer to struct type) that embeds *s1 automatically (implicitly) implements the S1 interface, and similarly any pointer to struct type that embeds s1 also implements S1 (and therefore *s2 and *s3 in your example).

If you export the fields, then you can use the reflect package to swap the values:
type s1 struct {
A string `json:"a"`
B string `json:"b"`
}
...
func modifyStruct(s interface{}) {
v := reflect.ValueOf(s).Elem()
a := v.FieldByName("A")
b := v.FieldByName("B")
t := reflect.New(a.Type()).Elem()
t.Set(a)
a.Set(b)
b.Set(t)
}
The fields must be exported to use the reflect package because the reflect package cannot set unexported fields.
playground example

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

Extend Struct definition golang

I want to extend a struct definition from that of the other
type A struct {
name string
}
type B struct {
A
}
So here A is a sub struct of B. But I do not want that. Instead I want the B's definition to be the same as A without any nesting
var a A
var b B
fmt.Printf("%v, %v \t", a, b)
a.name = "john"
b.name = "rick"
fmt.Printf("%v, %v \t", a, b)
So output of this is {john} {{rick}}. I want it to be {john}{rick}
You can do:
type B A
However, if you do this, any methods you defined for A will not be available for B.

Confused by interface embeded in struct in golang

Like code here, when embedding an interface A into struct B, and then set A to aa which is an instance of AA. Both B and AA have element X, when call b.X I just got B.X. How can I get b.AA.X? I know this syntax is wired, but I just want to figure out how var b is stored in the memory, I tried some unsafe syntax, no way to get b.A.X.:
package main
import (
"fmt"
"unsafe"
)
type A interface {
Hello() string
}
type B struct {
A
X string
}
type AA struct {
num int
X string
}
func (aa AA) Hello() string {
return fmt.Sprintf("hello %d from %s", aa.num, aa.X)
}
func main() {
aa := AA{200, "golang"}
b := B{A: aa, X: "python"}
fmt.Println(b.X) // output: python
fmt.Printf("--->%d\n", ((*AA)(unsafe.Pointer(&b.A)).num)) // output: --->17667104
fmt.Printf("===>%+v\n", b.A) // output: ===>{num:200 X:golang}
}
Embedded fields are accessed by their type name, and fields of an interface are only accessible by asserting the underlying type, so b.A.(AA).X will get you field X of the underlying AA that's in b.A. But if you're needing to access fields, you probably shouldn't be embedding the interface type anyway, you should be embedding the concrete type.

What's the difference between these functions in golang?

I'm new to Go programming and wondering what's the difference (if any) there between
a.
func DoSomething(a *A) {
b = a
}
b.
func DoSomething(a A) {
b = &a
}
If you are actually asking what the difference of those b's are, one is a pointer to the object passed as an argument to DoSomething, and the other is a pointer to a copy of the object passed as an argument to DoSomething.
https://play.golang.org/p/ush0hDZsdE
type A struct {
f string
}
func DoSomethingPtr(a *A) {
b := a
b.f = "hi"
}
func DoSomething(a A) {
b := &a
b.f = "hey"
}
func main() {
x := A{"hello"}
DoSomething(x)
fmt.Println(x)
DoSomethingPtr(&x)
fmt.Println(x)
}
The variable b would be assigned a different value in each function. The values are different because one is passing a copied value and the other is passing a pointer to the original value in memory.
package main
import "fmt"
type A string
func DoSomethingPtr(a *A) {
fmt.Println(a)
}
func DoSomething(a A) {
fmt.Println(&a)
}
func main() {
x := A("hello")
DoSomething(x)
DoSomethingPtr(&x)
}
Here is the executable proof.
In general, these two functions will assign different values to b. The second one makes a copy of the argument, and so the a inside the function generally has a different memory address than whatever input is passed into the function. See this playground example
package main
type A struct{
x int
}
var b *A
func d(a *A) {
b = a
}
func e(a A) {
b = &a
}
func main() {
var a = A{3}
println(&a)
d(&a)
println(b)
e(a)
println(b)
}
Interestingly, if you make the type A an empty struct instead, and initialize var a = A{}, you actually see the same value for b in the println statements.
That's because for the empty-struct type, there can only really only ever be 1 value, and its immutable, so all instances of it share the same memory address?

How to initialise a pointer member in a struct type inline?

With a type:
type A struct {
B int, C *int
}
How do I initialise a pointer member to a non-zero value inline, without creating temporaries?
a := A{
B: 42,
C: ?,
}
For the specific example you've given you are limited in what you can do without introducing additional statements.
If you want C to point at an initialised integer variable, you will need an additional statement to define that variable since you can't take the address of an integer literal (i.e. &42 would be an error).
If you just want to initialise C as a pointer to a new zero value, you are in luck though and can set it to new(int).
If you were instead dealing with a different type that had an initialiser syntax, you would also been in luck. For example, if C was a pointer to a structure, you could initialise it to &TheStruct{...}.
If none of these are appropriate, and you are really only after code clarity for initialising *int variables, a helper function might fit your requirements. For example:
func makeIntPointer(v int) *int {
return &v
}
package main
import "fmt"
type A struct {
B int
C *int
}
func newint(i int) *int{
return &i
}
func main() {
c := newint(5)
a := &A{
B: 42,
C: c,
}
fmt.Printf(" %v" , *a.C)
fmt.Printf(" %#v" , a.C)
}
http://play.golang.org/p/s0HIMHoMRo

Resources