'How to create structs in Golang from other partial structs?

If I have a two different structs, that mostly overlap each other in regards to their property types, and I need to create one from the other, is there some more concise way to do so w/o having to verbosely set each and every property?

Example: https://go.dev/play/p/k4TUrWQ7JLD

package main

import (
    "fmt"
)

type Foo struct {
    A int64
    B string
    C []string
    D []int
    // many other properties
}

type Bar struct {
    A string
    B string
    C []int64
    D []int
    // many other properties
}

func getBarA(a int64) string {
    // somhow map an int64 from Foo to a string for Bar
    return "A"
}

func getBarC(a []string) []int64 {
    // somhow map an int64 from Foo to a string for Bar
    return []int64{1, 2, 3}
}

func getABarFromFoo(foo Foo) Bar {
    return Bar{
        A: getBarA(foo.A),
        B: foo.B,
        C: getBarC(foo.C),
        D: foo.D,
        // All the rest of possibly MANY properties :-(
    }
}

func main() {
    b := getABarFromFoo(Foo{})
    fmt.Println(b)
}

As you can imagine, this mapping can get very repetitive/verbose… is there some way to just do something simple like this? (yes, I know this isn’t proper Go)

b:= Bar{
  ...foo,
  A: getBarA(foo.A),
  B: getBarC(foo.C),
}

I'm looking/hoping/🙏 for is a way to essentially say:

"Give me a Bar, created entirely from the compatible properties of a given Foo instance.

And for [these] few incompatible properties, here's how to set those."



Solution 1:[1]

Well.

b:= Bar{
  ...foo,
  A: getBarA(foo.A),
  B: getBarC(foo.C),
}

You can't assign []int64 returned from getBarC to string B. You might want to assign it to C instead.

I think you can't copy like javascript using spread operator. Even if you could, this will cause build error "duplicate field name ". What you can do is to keep a field Foo inside Bar and then convert the instance of Foo, namely foo, to bar which is an instance of Bar.

package main

import (
    "fmt"
)

type Foo struct {
    A int64
    B string
    C []string
    D []int
    // many other properties
}

type Bar struct {
    Foo
    A string
    B string
    C []int64
    D []int
    // many other properties
}

func getBarA(a int64) string {
    // somhow map an int64 from Foo to a string for Bar
    return "A"
}

func getBarC(a []string) []int64 {
    return []int64{1, 2, 3}
}

func getABarFromFoo(foo Foo) Bar {
    bar := Bar{Foo: foo}
    bar.A = getBarA(foo.A)
    bar.C = getBarC(foo.C)
    return bar
}

func main() {
    b := getABarFromFoo(Foo{})
    fmt.Println(b) // just don't sweat over empty Foo inside b
}

Solution 2:[2]

If some fields of two different structures overlap somehow, the answer is Object Orientation.

You should think about the relationship between Foo and Bar.

For instance there may share a embed common substructure. But why?

You should only copy and convert parameters. You can serialize/deserialize it to convert from/to any type with given rules.

Perhaps Foo knowns how to convert to Bar. Or Bar have a constructor who accepts Foo. Or Base.

type Base struct{
B string
D [] int
}

type Foo struct {
Base
A string
C []int
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Partho KR
Solution 2 Tiago Peczenyj