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

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

Related

go generics: processing different struct types with same data member types [duplicate]

This question already has answers here:
How can I access a struct field with generics (type T has no field or method)?
(1 answer)
Generic function to work on different structs with common members from external package?
(1 answer)
Closed 3 months ago.
There are two struct types, Foo and Bar, with an int data member val. I am trying to write a generic function that can handle both types. I tried the following and this did not work.
package main
import "fmt"
type Foo struct {
val int
}
type Bar struct {
val int
}
func Add[T any](slice []T) int {
var sum int
for _, elem := range slice {
sum += elem.val
}
return sum
}
func Test() {
f1 := Foo{val: 2}
f2 := Foo{val: 2}
fslice := []Foo{f1, f2}
fsum := Add(fslice)
fmt.Printf("fsum = %d\n", fsum)
b1 := Bar{val: 3}
b2 := Bar{val: 3}
bslice := []Bar{b1, b2}
bsum := Add(bslice)
fmt.Printf("bsum = %d\n", bsum)
}
func main() {
Test()
}
The compiler throws the following error.
$ go run generics1.go
# command-line-arguments
./generics1.go:16:15: elem.val undefined (type T has no field or method val)
Go playground link: https://go.dev/play/p/mdOMH3xuwu7
What could be a possible way to approach this?
Per golang 1.18 release note
The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter's type set have a field f. We may remove this restriction in a future release.
You could define one GetVal() interface method to retrieve the val, and use this method as part of type constraint of generic.
Sample codes
type Foo struct {
val int
}
func (f Foo) GetVal() int {
return f.val
}
type Bar struct {
val int
}
func (b Bar) GetVal() int {
return b.val
}
type MyType interface {
Foo | Bar
GetVal() int
}
func Add[T MyType](slice []T) int {
var sum int
for _, elem := range slice {
sum += elem.GetVal()
}
return sum
}
https://go.dev/play/p/0eJZpqy7q8f

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

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

Finding an item by iterating through a Go list

I'm attempting to port some C to Go.
I'm essentially looking for something in Go akin to Haskell's find :: (a -> Bool) -> [a] -> Maybe a
I have (roughly) this C for finding an item in a "list" by iterating through it:
struct foo {
struct foo *next;
char *name;
}
struct foo *foo_list;
// Snip
struct foo *foo = NULL;
for (f = foo_list; f; f = f->next) {
if (!strcmp("bar", f->name) {
foo = f;
}
}
if (foo)
// Stuff
How can I do this nicely and idiomatically in Go?
The "list" is likely to be small; performance characteristics aren't particularly interesting.
Am I likely to want a slice, or a list? A "list" of Foos or *Foos?
I currently have the following, but I suspect it's not particularly "idiomatic Go"!
var FooTable *list.List
// Snip
var foo *Foo = nil
for e := FooTable.Front(); e != nil; e = e.Next() {
if e.Value.(*Foo).name == "bar" {
foo = e.Value.(*Foo)
break
}
}
For idiomatic Go you want a slice of pointers to Foo (though if Foo is very small you might choose just a slice of Foo), so
var foos []*Foo
And then for searching
var found *Foo
for _, foo := range foos {
if foo.name == "bar" {
found = foo
break
}
}
if found != nil {
// stuff
}
If you do this a lot you'll wrap it up with some types something like this
type Foos []*Foo
func (fs Foos) find(what string) (foo *Foo) {
for _, foo = range foos {
if foo.name == what {
return foo
}
}
return nil
}
Then you can do
var foos Foos
foo := foos.find("bar")
if foo != nil {
// something
}
PS Nice to answer a question for someone I've actually met!

Resources