修改具有特定字段的任何结构的功能

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