I am new to go (golang). That is why my question may be irrelevant (or impossible to answer).
I have created two structs. Both of these embed another struct. Now I want to update a field of the embedded struct inside a function.
package main
import (
"fmt"
"reflect"
"time"
)
type Model struct {
UpdatedAt time.Time
}
type Fruit struct {
Model
label string
}
type Animal struct {
Model
label string
}
func update(v interface{}) {
reflectType := reflect.TypeOf(v)
reflectKind := reflectType.Kind()
if reflectKind == reflect.Ptr {
reflectType = reflectType.Elem()
}
m := reflect.Zero(reflectType)
fmt.Println(m)
}
func main() {
apple := &Fruit{
label: "Apple",
}
tiger := &Animal{
label: "Tiger",
}
update(apple)
update(tiger)
fmt.Println(apple)
fmt.Println(tiger)
}
I wish to implement the update function so that it will put the current time in UpdatedAt of the passed struct. But I am not able to do this.
In this case, the field of Fruit and Animal is same: label. But it will not always be. Please keep that in mind when providing your suggestions.
Any guidance would be much appreciated.
I'd avoid reflect or interface{} if you're starting to learn go. Beginners usually fall back on them like a void * crutch. Try to use concrete types or well defined interfaces.
This should get you going:
type Timestamper interface {
Update()
UpdatedTime() time.Time
}
type Model struct {
updated time.Time
}
func (m *Model) Update() { m.updated = time.Now() }
func (m *Model) UpdatedTime() time.Time { return m.updated }
type Fruit struct {
Model
label string
}
type Animal struct {
Model
label string
}
// update will work with a `Model` `Animal` or `Fruit`
// as they all implement the `Timestamper` interface`
func update(v Timestamper) {
v.Update()
}
Playground: https://play.golang.org/p/27yDVLr-zqd
Assuming you want to achieve this via reflection: first of all, you have to pass a pointer to the struct. You're now passing a copy of the struct, so any modifications done in update will be done on the copy, not on the instance you passed in. Then, you can lookup the field UpdatedAt in the interface passed in, and set it.
That said, that's probably not the best way to do this. Another way of doing this without reflection is:
func update(in *Model) {
in.UpdatedAt = time.Now()
}
func main() {
apple := &Fruit{}
update(&apple.Model)
}
Or:
func (in *Model) update() {
in.UpdatedAt = time.Now()
}
func main() {
apple := &Fruit{}
apple.update()
}
Related
Foreign application API gives me a list of names in JSON format. I need modify all of those.
But I do not like to write some loop for it (especially after Python using with reflection and stuff)
Is there any method to write something like this in Go?
type MyIncredibleType struct {
Name ModifyName // ModifyName is not a type!
}
func ModifyName(input string) string {
return input + ".com"
}
The expected behavior of this is:
a := MyIncredibleType{Name: "Abracadabra"}
print(a.Name) // Abracadabra.com
This seems pretty straight forward to me, assuming I understand your question correctly:
// ModifyName func
func ModifyName(input string) string {
return fmt.Sprintf("%v.com", input)
}
If you wish to achieve this within the type itself, without modifying (mutating) the internal state:
type MyType sturct {
name string // unexported
}
// accessor func to return name
func (t MyType) Name() string {
return t.name
}
// accessor func to return modified name
func (t MyType) ModifiedName() string {
return fmt.Sprintf("%v.com", t.name)
}
If you want to modify the internal state:
type MyType struct {
name string
}
// mutator func (note the pointer for pass by reference)
func (t *MyType) ModifyName(input string) {
t.name = fmt.Sprintf("%v.com", input)
}
// accessor (note no pointer for pass by value)
func (t MyType) Name() string {
return t.name
}
This is is not possible in GO. That's not how struct works in Go.
type MyIncredibleType struct {
Name ModifyName `json:"name"` // ModifyName is not a type!
}
you can only define Built-in types for your fields of struct or you can define Composite Literal types.
Composite literals construct values for structs, arrays, slices, and
maps and create a new value each time they are evaluated. They consist
of the type of the literal followed by a brace-bound list of elements.
Each element may optionally be preceded by a corresponding key.
Try to create a method receiver of struct which you are using to parse json coming from the api to modify the name. That will let you achieve something similar to what you want.
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func(myIncredibleType *MyIncredibleType) ModifyName() string {
return myIncredibleType.Name+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := a.ModifyName()
fmt.Printf("%s",name)
}
Playground Example
Or you can pass an interface which will wrap any struct value with name field and then use Type assertion to get the underlying value to modify the same and return the result:
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func ModifyName(input interface{}) string{
return input.(interface{}).(string)+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := ModifyName(a.Name)
fmt.Printf("%s",name)
}
Working code on Go Playground
For more information also go through Golang method Declarations on how to create receivers.
I have a function that has a parameter with the type interface{}, something like:
func LoadTemplate(templateData interface{}) {
In my case, templateData is a struct, but each time it has a different structure. I used the type "interface{}" because it allows me to send all kind of data.
I'm using this templateData to send the data to the template:
err := tmpl.ExecuteTemplate(w, baseTemplateName, templateData)
But now I want to append some new data and I don't know how to do it because the "interface" type doesn't allow me to add/append anything.
I tried to convert the interface to a struct, but I don't know how to append data to a struct with an unknown structure.
If I use the following function I can see the interface's data:
templateData = appendAssetsToTemplateData(templateData)
func appendAssetsToTemplateData(t interface{}) interface{} {
switch reflect.TypeOf(t).Kind() {
case reflect.Struct:
fmt.Println("struct")
s := reflect.ValueOf(t)
fmt.Println(s)
//create a new struct based on current interface data
}
return t
}
Any idea how can I append a child to the initial interface parameter (templateData)? Or how can I transform it to a struct or something else in order to append the new child/data?
Adrian is correct. To take it a step further, you can only do anything with interfaces if you know the type that implements that interface. The empty interface, interface{} isn't really an "anything" value like is commonly misunderstood; it is just an interface that is immediately satisfied by all types.
Therefore, you can only get values from it or create a new "interface" with added values by knowing the type satisfying the empty interface before and after the addition.
The closest you can come to doing what you want, given the static typing, is by embedding the before type in the after type, so that everything can still be accessed at the root of the after type. The following illustrates this.
https://play.golang.org/p/JdF7Uevlqp
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
type After struct {
Before
s []string
}
func contrivedAfter(b interface{}) interface{} {
return After{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b).(After)
fmt.Println(a.m)
fmt.Println(a.s)
}
Additionally, since the data you are passing to the template does not require you to specify the type, you could use an anonymous struct to accomplish something very similar.
https://play.golang.org/p/3KUfHULR84
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
func contrivedAfter(b interface{}) interface{} {
return struct{
Before
s []string
}{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b)
fmt.Println(a)
}
You can't append data arbitrarily to a struct; they're statically typed. You can only assign values to the fields defined for that specific struct type. Your best bet is probably to use a map instead of structs for this.
Not recommended, but you can create structs dynamically using the reflect package.
Here is an example:
package main
import (
"encoding/json"
"os"
"reflect"
)
type S struct {
Name string
}
type D struct {
Pants bool
}
func main() {
a := Combine(&S{"Bob"}, &D{true})
json.NewEncoder(os.Stderr).Encode(a)
}
func Combine(v ...interface{}) interface{} {
f := make([]reflect.StructField, len(v))
for i, u := range v {
f[i].Type = reflect.TypeOf(u)
f[i].Anonymous = true
}
r := reflect.New(reflect.StructOf(f)).Elem()
for i, u := range v {
r.Field(i).Set(reflect.ValueOf(u))
}
return r.Addr().Interface()
}
You could use something like the Combine function above to shmush any number of structs together. Unfortunately, from the documentation:
StructOf currently does not generate wrapper methods for embedded fields. This limitation may be lifted in a future version.
So your created struct won't inherit methods from the embedded types. Still, maybe it does what you need.
If you are just looking to convert your interface to struct, use this method.
type Customer struct {
Name string `json:"name"`
}
func main() {
// create a customer, add it to DTO object and marshal it
receivedData := somefunc() //returns interface
//Attempt to unmarshall our customer
receivedCustomer := getCustomerFromDTO(receivedData)
fmt.Println(receivedCustomer)
}
func getCustomerFromDTO(data interface{}) Customer {
m := data.(map[string]interface{})
customer := Customer{}
if name, ok := m["name"].(string); ok {
customer.Name = name
}
return customer
}
Below I have an example of one structure which embeds another. I'm trying to figure out how to pass the more specific structure pointer to be stored in a less specific one. You can think of it as a collection. Wrapping in an interface doesn't seem to work, as doing so would make a copy, which isn't valid for structs with locks. Ideas?
package stackoverflow
import "sync"
type CoolerThingWithLock struct {
fancyStuff string
ThingWithLock
}
func NewCoolerThingWithLock() *CoolerThingWithLock {
coolerThingWithLock := &CoolerThingWithLock{}
coolerThingWithLock.InitThingWithLock()
return coolerThingWithLock
}
type ThingWithLock struct {
value int
lock sync.Mutex
children []*ThingWithLock
}
func (thingWithLock *ThingWithLock) InitThingWithLock() {
thingWithLock.children = make([]*ThingWithLock, 0)
}
func NewThingWithLock() *ThingWithLock {
newThingWithLock := &ThingWithLock{}
newThingWithLock.InitThingWithLock()
return newThingWithLock
}
func (thingWithLock *ThingWithLock) AddChild(newChild *ThingWithLock) {
thingWithLock.children = append(thingWithLock.children, newChild)
}
func (thingWithLock *ThingWithLock) SetValue(newValue int) {
thingWithLock.lock.Lock()
defer thingWithLock.lock.Unlock()
thingWithLock.value = newValue
for _, child := range thingWithLock.children {
child.SetValue(newValue)
}
}
func main() {
thingOne := NewThingWithLock()
thingTwo := NewCoolerThingWithLock()
thingOne.AddChild(thingTwo)
thingOne.SetValue(42)
}
Error: cannot use thingTwo (type *CoolerThingWithLock) as type
*ThingWithLock in argument to thingOne.AddChild
It's impossible to store the wrapping type in []*ThignWithLock since go has no notion of structural subtyping.
Your assertion that an interface will result in copying is incorrect, and you can get the desired effect by doing:
type InterfaceOfThingThatParticipatesInAHierarchy interface {
AddChild(InterfaceOfThingThatParticipatesInAHierarchy)
SetValue(int)
}
type ThingWithLock struct {
...
children []InterfaceOfThingThatParticipatesInAHierarchy
}
func (thingWithLock *ThingWithLock) AddChild(newChild InterfaceOfThingThatParticipatesInAHierarchy) { ... }
As long as the interface is implemented on a *ThingWithLock and not ThingWithLock, there will be no copying of the receiver struct itself, only the pointer to the struct will be copied on the stack.
I'm wondering if it's possible to get the method fields from a typed function using reflection or by other means.
The problem that I'm trying to solve is that I have a method which accepts a function of specific type but I need to actually transport different types and based on the type provided to execute operations. I'm aware that I could use an interface{} value as receiver but I don't want to loose the type checking on the calling function ("GetIt")
package main
type ttp struct {
Coupons string
}
func (m ttp) GetIt(x string) {
if m.Coupons != "" {
print(m.Coupons)
}
}
func calculate(mth func(s string)){
//perform calculations and update the Coupon field
mth.Cupons = "one coupon" // is not working :(
// execute it again with the processed value
mth.GetIt() // is not working
}
func main() {
m := ttp{Coupons: "something"}
calculate(m.GetIt)
}
Play
well this does not answer your exact question but it should solve your problem and cleanup the logic: push the calculate method inside the type and use a interface
https://play.golang.org/p/On_AigRYW6
package main
import "fmt"
type Computer interface {
Compute(string)
}
type myp struct {
Coupons string
}
// myp implements Computer
func (m *myp) Compute(x string) {
m.GetIt(x)
fmt.Println("myp")
}
type ttp struct {
Various string
}
// ttp implements Computer
func (m *ttp) Compute(x string) {
m.GetIt(x)
fmt.Println("ttp")
}
func (m myp) GetIt(x string) {}
func (m ttp) GetIt(x string) {}
func main() {
m := &myp{Coupons: "something"}
t := &ttp{Various: "various stuff"}
var stuff = []Computer{m, t}
for _, c := range stuff {
c.Compute("s")
}
}
I have the following struct in my package.
type Animal struct {
Id string
// and some more common fields
}
A user who uses my package should be able to implement own Animals. As I need Id in one of my methods the user has to embed my struct.
type Dog struct {
Animal
Name string
Legs int
}
In my package I have a save() function that saves Animals to database. As I don't know the user's type I have to use interface{} as argument type. My question is: How do I get the Id (from the parent struct Animal)? At the moment I use some JSON parsing and unmarshalling, but is this the way to go/Go?
func put(doc interface{}) error {
res, err := json.Marshal(doc)
if err != nil {
return err
}
var animal *Animal
err = json.Unmarshal(res, &animal)
if err != nil {
return err
}
fmt.Println(animal.Id) // finally I have the Id
// ...
}
Usage
func main() {
bello := Dog{
Animal: Animal{
Id: "abcd1234",
},
Name: "bello",
Legs: 4,
}
err := put(bello)
// ...
}
Maybe you can add an interface in order to be sure to get a reference to the parent struct:
type AnimalGetter interface {
GetAnimal() *Animal
}
func (dog *Dog) GetAnimal() *Animal {
return &dog.Animal
}
That would allow your save method to do a type assertion (AnimalGetter):
func save(obj interface{}) {
animal := obj.(AnimalGetter)
fmt.Printf("%v\n", animal.GetAnimal())
}
See a complete example in play.golang.org.
Output:
&{{dogid} wouf 0}
&{dogid}
Simpler:
func save(animal AnimalGetter) {
fmt.Printf("%v\n", animal.GetAnimal())
}
(play.golang.org)
You can do it with this small reflection trick, although perhaps using interfaces like #VonC suggested might be a more practical and idiomatic approach.
Anyway, here's the same achieved with reflection:
func main() {
d := Dog {
Animal { "id1111" },
"Barky",
4,
}
fmt.Println(reflect.ValueOf(d).FieldByName("Animal").Interface().(Animal))
}
The way to go/Go here is to declare Animal as an interface:
type Animal interface {
ID() int
Name() string
// Other Animal field getters here.
}
Then, save can take Animal as an argument and get all the info it needs using Animal's methods.
The other possbile way is to use the reflect package to obtain the Animal fields from the struct, but this will be buggier, dirtier and possibly slower that using an interface.