Struct conversion with methods in golang - go

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

Related

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
}

Cannot use variable of type *T as type in argument

I'm learning Go 1.18 generics and I'm trying to understand why I'm having trouble here. Long story short, I'm trying to Unmarshal a protobuf and I want the parameter type in blah to "just work". I've simplified the problem as best I could, and this particular code is reproducing the same error message I'm seeing:
./prog.go:31:5: cannot use t (variable of type *T) as type stringer in argument to do:
*T does not implement stringer (type *T is pointer to type parameter, not type parameter)
package main
import "fmt"
type stringer interface {
a() string
}
type foo struct{}
func (f *foo) a() string {
return "foo"
}
type bar struct{}
func (b *bar) a() string {
return "bar"
}
type FooBar interface {
foo | bar
}
func do(s stringer) {
fmt.Println(s.a())
}
func blah[T FooBar]() {
t := &T{}
do(t)
}
func main() {
blah[foo]()
}
I realize that I can completely simplify this example by not using generics (i.e., pass the instance to blah(s stringer) {do(s)}. However, I do want to understand why the error is happening.
What do I need to change with this code so that I can create an instance of T and pass that pointer to a function expecting a particular method signature?
In your code there's no relationship between the constraints FooBar and stringer. Furthermore the methods are implemented on the pointer receivers.
A quick and dirty fix for your contrived program is simply to assert that *T is indeed a stringer:
func blah[T FooBar]() {
t := new(T)
do(any(t).(stringer))
}
Playground: https://go.dev/play/p/zmVX56T9LZx
But this forgoes type safety, and could panic at run time. To preserve compile time type safety, another solution that somewhat preserves your programs' semantics would be this:
type FooBar[T foo | bar] interface {
*T
stringer
}
func blah[T foo | bar, U FooBar[T]]() {
var t T
do(U(&t))
}
So what's going on here?
First, the relationship between a type parameter and its constraint is not identity: T is not FooBar. You cannot use T like it was FooBar, therefore *T is definitely not equivalent to *foo or *bar.
So when you call do(t), you're attempting to pass a type *T into something that expects a stringer, but T, pointer or not, just does not inherently have the a() string method in its type set.
Step 1: add the method a() string into the FooBar interface (by embedding stringer):
type FooBar interface {
foo | bar
stringer
}
But that's not enough yet, because now none of your types actually implement it. Both declare the method on the pointer receiver.
Step 2: change the types in the union to be pointers:
type FooBar interface {
*foo | *bar
stringer
}
This constraint now works, but you have another problem. You can't declare composite literals when the constraint doesn't have a core type. So t := T{} is also invalid. We change it to:
func blah[T FooBar]() {
var t T // already pointer type
do(t)
}
Now this compiles, but t is actually the zero value of a pointer type, so it's nil. Your program doesn't crash because the methods just return some string literal.
If you need to also initialize the memory referenced by the pointers, inside blah you need to know about the base types.
Step 3: So you add T foo | bar as one type param, and change the signature to:
func blah[T foo | bar, U FooBar]() {
var t T
do(U(&t))
}
Done? Not yet. The conversion U(&t) is still invalid because the type set of both U and T don't match. You need to now parametrize FooBar in T.
Step 4: basically you extract FooBar's union into a type param, so that at compile time its type set will include only one of the two types:
type FooBar[T foo | bar] interface {
*T
stringer
}
The constraint now can be instantiated with T foo | bar, preserve type safety, pointer semantics and initialize T to non-nil.
func (f *foo) a() string {
fmt.Println("foo nil:", f == nil)
return "foo"
}
func main() {
blah[foo]()
}
Prints:
foo nil: false
foo
Playground: https://go.dev/play/p/src2sDSwe5H
If you can instantiate blah with pointer types, or even better pass arguments to it, you can remove all the intermediate trickery:
type FooBar interface {
*foo | *bar
stringer
}
func blah[T FooBar](t T) {
do(t)
}
func main() {
blah(&foo{})
}

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.

Methods with receivers of pointer type won't list in reflection

I was trying to generalize my code by using reflection to call all methods of a type. It's easy and straightforward but there is a problem with that, reflection.TypeOf(T).NumMethods (or other methods) ignores methods which used receiver type as a pointer.
For example this small code will print 1 instead of 2:
package main
import (
"fmt"
"reflect"
)
type Foo struct {}
func (f Foo) Bar() {}
func (f *Foo) Baz() {}
func main() {
obj := Foo{}
fmt.Println(reflect.TypeOf(obj).NumMethod())
}
You can run in playground.
It prints 1 because of Bar method. If you delete the pointer (*) from Baz, it will print 2.
My question is how can I list all methods regardless of receiver type.
Thanks
Get pointer to method with pointer type receiver. In case you wants to call the method by name using reflection here is the code.
package main
import (
"fmt"
"reflect"
)
type Foo struct{}
func (f Foo) Bar() {
fmt.Println("Inside Bar")
}
func (f *Foo) Baz() {
fmt.Println("Inside Baz")
}
func main() {
rfl := reflect.ValueOf(&Foo{})
v := rfl.MethodByName("Baz")
results := v.Call(nil)
fmt.Printf("%#v\n", results)
fmt.Println(reflect.TypeOf(&Foo{}).NumMethod())
}
Go Playground
You can just use the pointer of obj to get all Methods:
func main() {
obj := &Foo{}
fmt.Println(reflect.TypeOf(obj).NumMethod())
}
playground

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