How to create structs in Golang from other partial structs? - go

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."

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
}

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
}

Related

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.

Add struct or pointer to struct to slice

Say I have structs like so:
type Foo struct {
F string `zoom:"1"`
}
type Bar struct {
F string `zoom:"2"`
}
type Baz struct {
F string `zoom:"3"`
}
Say I wanted to create a func that can extract the f field from each struct, it might look like:
func extractField(s []struct{}){
for _, v := range s {
t := reflect.TypeOf(v{})
f, _ := t.FieldByName("F")
v, ok := f.Tag.Lookup("zoom")
}
}
is there a way to pass the structs to extractField? If I do this:
extractField([]struct{}{Foo, Bar, Baz})
I get this error:
Type Foo is not an expression
Type Bar is not an expression
Type Baz is not an expression
I just want to pass the 3 structs to the extractField func.
The only way I could figure out how to do this, is like so:
type Foo struct {
F string `zoom:"1"`
}
type Bar struct {
F string `zoom:"2"`
}
type Baz struct {
F string `zoom:"3"`
}
func extractField(s []interface{}){
for _, v := range s {
t := reflect.TypeOf(v)
f, _ := t.FieldByName("F")
v, ok := f.Tag.Lookup("zoom")
fmt.Println(v,ok)
}
}
func main(){
extractField([]interface{}{Foo{},Bar{},Baz{}}) // <<<< here
}
not sure if there is a way to pass a struct without "instantiating it" first.
The original code looks like it follows a JavaScript approach, where a function would mutate an object. Go is a little bit different, where it's more common to self-mutate.
For example:
type Generic struct {
field string
}
func (generic *Generic) Value () string {
return generic.field
}
someObject := &Generic{
field: "some value",
}
log.Print(someObject.Value()) // Outputs "some value"
If you're coming from the JavaScript world, think of structs a little bit like an object/class that can contain attributes and functions. Structs are merely a definition until an instance is instantiated. This differs from JavaScript where the definition of the object and the data are both defined at the same time.
As the other answer points out, an interface is another similar approach to dealing with this.
Clarification
In JavaScript terms, the OP is attempting to do something akin to:
class Foo {...}
class Bar {...}
class Baz {...}
extractField(Foo, Bar, Baz)
In JS-speak, this would pass a class definition to the extractField method. You would still have to instantiate an instance of a class if you want to manipulate/read from it, like:
extractField(new Foo(), new Bar(), new Baz())
This is basically what is being accomplished with
extractField([]interface{}{Foo{},Bar{},Baz{}})
I think the problem you're running into is the Foo/Bar/Baz structs are instantiated, but the nested F struct is not.
The overall problem seems like a much better match for an interface type.
type HasF interface {
GetF() string
}
It's easy enough to define these methods, e.g.
func (foo Foo) GetF() string { return foo.F }
Your method to iterate over them becomes almost trivial
func extractField(s []HasF) {
for _, v := range s {
fmt.Printf(v.GetF())
}
}
func main() {
extractField([]HasF{Foo{},Bar{},Baz{}})
}
https://play.golang.org/p/uw0T7TGVC0n has a complete version of this.

Struct containing embedded slice of struct

I have this code
package main
import (
"fmt"
)
type foo struct {
a int
b bool
}
type foos []foo
type bar struct {
foos
}
func newBar() *bar {
b := &bar{
foos: make([]foo, 3, 3),
}
for _, foo := range b.foos {
// didn't set b to true
foo.b = true
}
return b
}
func main() {
b := newBar()
fmt.Println(b)
// set b to true
b.foos[0].b = true
fmt.Println(b)
}
The Go Playground
As you can see I want to initialize bar using constructor newBar() but I want the embed type foo.b is initialize with non zero value so I initialize with for range statement but it didn't work as intended, the foo.b is still false, all of them. As comparison in the main function using this code b.foos[0].b = true it work. So whats wrong with my code?
Omg, I just realized this after posting this question, it's because variable slot is local to for loop. So the solution is:
for i, _ := range b.foos {
// now b is set to true
b.foos[i].b = true
}

Struct conversion with methods in golang

In order to simplify imports and dependencies for a project, I would like to convert a type struct and still have access to all the methods it is attached to.
This is what I am looking for :
type foo struct {
a int
}
func (f *foo) bar() {
f.a = 42
}
type foo2 foo
func main() {
f := foo{12}
f.bar()
f2 := foo2(f)
f2.a = 0
f2.bar()
fmt.Println(f)
fmt.Println(f2)
}
On the line "f2.Bar()" I get the error :
"f2.Bar undefined (type Foo2 has no field or method Bar)"
How can I do to have access to the method Bar even if I made a conversion. I would like my output to be
{42}
{42}
You can use struct embeding
package main
import (
"fmt"
)
type foo struct {
a int
}
func (f *foo) bar() {
f.a = 42
}
type foo2 struct {
foo
}
func main() {
f := foo{12}
f.bar()
f2 := foo2{}
f2.a = 0
f2.bar()
fmt.Println(f)
fmt.Println(f2)
}
Just create struct and use foo as one of its members. Don't give it explicit name
type foo2 struct {
foo
}
That way all methods of foo will be available for foo2.
Note that output of this program will be:
{42}
{{42}}
More effective way of achieving what I suppose you want to do, will come with new Go 1.9: https://tip.golang.org/doc/go1.9#language

How do I assign embedded interface to object in golang [duplicate]

This question already has answers here:
Type converting slices of interfaces
(9 answers)
Closed 5 years ago.
I am attempting to assign an array of structs of type Baz to an array of interfaces of type Bar embedded in a second struct of type Foo. I have not been able to find the exact information here or elsewhere googling. I have provided a minimum working example.
I get the following error:
$ go run main.go
./main.go:38: cannot use baz (type []*Baz) as type []Bar in argument to NewFoo
My apologies if my code is not idiomatic go and also if I have not confirmed exactly to standards for posting questions, this is my first post.
package main
import (
"fmt"
)
type Foo struct {
b []Bar
}
type Baz struct {
a, b int
}
type Bar interface {
Multiply() int
}
func (baz *Baz) Multiply() int{
return baz.a * baz.b
}
func NewFoo(bar []Bar) *Foo{
return &Foo{b: bar}
}
func NewBaz() []*Baz {
bazes := make([]*Baz, 2)
bazes[0] = &Baz{a: 1, b: 2}
bazes[1] = &Baz{a: 3, b: 4}
return bazes
}
func main() {
baz := NewBaz()
foo := NewFoo(baz)
for _, f := range foo.b {
fmt.Println("Foo.Multiply ", f.Multiply())
}
}
UPDATE: I approved the duplicate vote after further reading and understanding the suggested related posts. Thank you to those who pointed me in that direction. For future interested readers, my final implementation consistent with my use case is as follows:
package main
import (
"fmt"
)
type Foo struct {
b []Bar
}
type Baz struct {
a, b int
}
type Bar interface { //
Multiply() int
}
func (baz *Baz) Multiply() int {
return baz.a * baz.b
}
func NewFoo(bar []*Baz) *Foo{
f := &Foo{}
f.b = make([]Bar, 2)
fmt.Println("len(bar) ", len(bar), "len(f.b)", len(f.b) )
for i, _ := range f.b {
f.b[i] = bar[i]
}
return f
}
func MakeBaz() []*Baz {
bazes := make([]*Baz, 2)
bazes[0] = NewBaz(1, 2)
bazes[1] = NewBaz(3, 4)
return bazes
}
func NewBaz(aa, bb int) *Baz {
return &Baz{a: aa, b: bb}
}
func main() {
baz := MakeBaz()
foo := NewFoo(baz)
fmt.Printf("%v\n", foo)
for _, f := range foo.b {
fmt.Println("Foo.Multiply ", f.Multiply())
}
}
There's a couple of different ways to do this, but the most straightforward is to embed type Baz with Bar. Baz can now multiply() and has two fields (a, b). Your code would look something like this:
package main
import (
"fmt"
)
type Foo struct {
b []*Baz
}
type Baz struct {
Bar
a, b int
}
type Bar interface {
Multiply() int
}
func (baz *Baz) Multiply() int {
return baz.a * baz.b
}
func NewFoo(bar []*Baz) *Foo {
return &Foo{b: bar}
}
func NewBaz() []*Baz {
bazes := make([]*Baz, 2)
bazes[0] = &Baz{a: 1, b: 2}
bazes[1] = &Baz{a: 3, b: 4}
return bazes
}
func main() {
baz := NewBaz()
foo := NewFoo(baz)
for _, f := range foo.b {
fmt.Println("Foo.Multiply ", f.Multiply())
}
}
GoPlay here:
https://play.golang.org/p/lia0ZS81TO
Great first question, by the way.
The error you are getting:
cannot use baz (type []*Baz) as type []Bar
Is because Go won't let you assign an array of Baz to an array of Bar, even when it'd let you assign a Baz to a Bar variable.
To see this in a more simple form, this works fine:
var oneBaz *Baz = &Baz{a: 1, b: 2}
var oneBar Bar = oneBaz
Because the type Baz satisfies the Bar interface and you can assign a Baz object to a Bar variable.
But this does not work (even if it'd looks like it could work):
var bazArray []*Baz = make([]*Baz, 2)
var barArray []Bar = bazArray
It will throw the same error you are seeing.
In order to make this work, you'd probably need to implement one of the solutions listed in this related question:
Type converting slices of interfaces in go

Resources