Spread operator analogue - go

I have a struct and the instance of that struct:
type Obj struct {
ssid string
code string
mit string
// and other props (23)
}
var ValidObject = Obj {
ssid: "AK93-KADJ9-92J76",
code: "SKO-120O"
mit: "MSLA-923-OKSW"
}
I want to create a slice of structs (Obj) which will contain ValidObject with only some fields changed. I think the best way to explain that would be using pseudo code, so here it is (using spread operator from JS :) ):
var slc = []Obj{
{
...ValidObject,
code: "Other value",
},
{
...ValidObject,
mit: "Other value"
}
}

Create a helper function that takes an Object, changes its code and returns the new Object:
func withCode(obj Obj, code string) Obj {
obj.code = code
return obj
}
Note that withCode takes a non-pointer value, so the Object you pass will not be modified, only the local copy.
And using this your task is:
var slc = []Obj{
withCode(ValidObject, "Other value"),
withCode(ValidObject, "Yet another value"),
}
fmt.Println(slc)
Output (try it on the Go Playground):
[{AK93-KADJ9-92J76 Other value MSLA-923-OKSW}
{AK93-KADJ9-92J76 Yet another value MSLA-923-OKSW}]
This helper withCode could even be a method (not a function).
Note that if you need to have variations of many fields, it would probably be better to add these as methods, so you can chain the calls.
For example:
func (o Obj) withCode(code string) Obj {
o.code = code
return o
}
func (o Obj) withSsid(ssid string) Obj {
o.ssid = ssid
return o
}
func (o Obj) withMit(mit string) Obj {
o.mit = mit
return o
}
And then using it:
var slc = []Obj{
ValidObject.withCode("code2").withSsid("ssid2"),
ValidObject.withMit("mit2").withSsid("ssid3"),
}
fmt.Println(slc)
Output (try it on the Go Playground):
[{ssid2 code2 MSLA-923-OKSW} {ssid3 SKO-120O mit2}]

Create a slice of struct var objCollector = []obj{} globally and read data into the defined struct and append the object into slice of struct that we created.
type Obj struct {
ssid string
code string
mit string
}
var objCollector = []obj{}
func main() {
var ValidObject = Obj{
ssid: "AK93-KADJ9-92J76",
code: "SKO-120O",
mit: "MSLA-923-OKSW",
}
append(objectCollector, ValidObject)
}

Related

How to update struct fields in a type switch in Go?

I want to make a function that takes in different commodity Struct and encrypts their ids.
I have a encryption function:
func encryption(in string, typeOf string) string {
result := in + typeOf
return result
}
and 2 commodity Struct:
type cloth struct {
clothId string
name string
}
type pants struct {
pantsId string
name string
}
I want to modify them through a function, by passing in variadic parameters, to perform different operations, it should modify the id of the Struct
func encryptId(in interface{}) {
switch in.(type) {
case cloth:
in.clothId = encryption(in.clothId, "cloth")
case pants:
in.pantsId = encryption(in.pantsId, "pants")
default:
return
}
}
But although this function can determine the type, it cannot modify them, how can I do?
I found these online, but it can only judge and not modify.
I hope after doing this
func main() {
clo := cloth{"cloth", "blue"}
pan := pants{"pants", "green"}
encryptId(clo)
encryptId(pan)
fmt.Println(clo)
fmt.Println(pan)
}
it will output
{clothcloth blue}
{pantspants green}
Solved by mkopriva
It cannot modify them because they are copies. Use pointers instead.
E.g. encryptId(&clo) and inside the function's switch use case *cloth:
go.dev/play/p/4uYzqZ0BKVy (note the assignment in the switch
statement, i.e. switch in := in.(type) {)
func encryptId(in interface{}) {
switch s := in.(type) {
case *cloth:
s.id = encryption(s.id, "key")
case *pants:
s.id = encryption(s.id, "key")
default:
return
}
}
func main() {
clo := cloth{"cloth", "blue"}
pan := pants{"pants", "green"}
encryptId(&clo)
encryptId(&pan)
fmt.Println(clo)
fmt.Println(pan)
}

Proper way to return string pointers in Graphql API

I'm building a graphql API using 99designs/gqlgen but I'm a bit confused about the proper way of returning pointers.
The graphql type
type GraphType {
image_url: String
}
The go code is:
type GraphType struct {
ImageURL *string `json:"image"`
}
type T struct {
value string
}
func (t T) toImageUrl() string {
return fmt.Sprintf("http://test.localhost/%s", t.value)
}
func (t T) toGraphType() *GraphType {
var items = &GraphType{
}
return items
}
There a 3 ways that I can do this
// toImageUrl returns a pointer
func (t T) toImageUrl() *string {
image := fmt.Sprintf("http://test.localhost/%s", t.value)
return &image
}
var items = &GraphType{
ImageURL: t.toImageUrl(),
}
// store the value and get a pointer
image := t.toImageUrl()
var items = &GraphType{
ImageURL: &image,
}
// make a utility function for poiters
func getPointerString(s string) *string {
return &s
}
var items = &GraphType{
ImageURL: getPointerString(t.toImageUrl()),
}
The easyest is to use getPointerString but I don't know what happens to the momory usages, is this memory safe?
Unlike other languages you can safely return a pointer to a local variable due to Go's amazing "escape analysis". So yes, using getPointerString is safe.
But I really don't think you need to return NULL. So you don't need a nullable string in your schema. Simply use String! instead of String then you can just return a Go string instead of needing a pointer to the string. Ie:
type GraphType {
image_url: String!
}
type GraphType struct {
ImageURL string `json:"image"`
}

Is there a more idiomatic approach to creating variables of specific types based on input?

In this playground link I have created a contrived version of my code where I am creating a variable of type X based on an input string. The variable will be one of a handful of types and implement an interface.
The code currently compiles and provides the correct result, however it strikes me as quite verbose and I'm trying to find if there is a shorthand approach to the result I'm achieving. The example has 3 types (dog, cat & bird) that implement the interface (animal), however my actual code will have up to 40 types in this switch statement.
The reason I am using this code is when retrieving results form a DBMS, I'm trying to use a generic load method that, when combined with sqlx, loads the database table into the correct struct based on the input string. I have entire control over the application and can change the input string to another type if required.
Code from playground link:
package main
import (
"fmt"
)
type animal interface {
call() string
}
type dog struct {
}
func (d *dog) call() string {
return "Woof!"
}
type cat struct {
}
func (c *cat) call() string {
return "Meow!"
}
type bird struct {
}
func (c *bird) call() string {
return "Chirp!"
}
func main() {
var animal animal
animalType := "dog"
switch animalType{
case "dog":
animal = new(dog)
case "cat":
animal = new(cat)
case "bird":
animal = new(bird)
You can create a hashmap from "string" to "function that returns animal" but setting that up would be more verbose than the switch statement.
Something like this (not tested)
type AnimalCtor func() animal
var animalMap map[string]AnimalCtor
.....
func init() {
animalMap["dog"] = func() animal { return &dog{} }
animalMap["cat"] = func() animal { return &cat{} }
animalMap["bird"] = func() animal { return &bird{} }
.....
}
func createAnimalFromString(input string) animal {
ctor, ok := animalMap[input]
if ok {
return ctor()
} else {
return nil
}
}
But it's a lot more verbose than the switch statement and obscures what should otherwise be explicit and clear.

Assigning type / creating struct via switch statement

I'm having a bit of trouble figuring out how to either create a struct in a switch statement or assign a type to it in a switch statement.
Here's some non-working code illustrating what I'm trying to do:
var result
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
result.Print()
I think something involving an empty interface{} might be related to solving this issue but unfortunately I'm still a bit ignorant with golang and I'm not seeing how to make it work.
Here's a link to a slightly modified version of the code for more context: https://play.golang.org/p/Rb1oaMuvmU2
The issue is not defining the print function, it's assigning a specific type of struct to the result variable based on using the individual Print function that the struct implements.
Let me know if there's any more info I could provide.
Since you are calling .Print, you need to use an interface which has that method. I think you are looking for something like
type Printer interface {
Print()
}
func (s *struct1) Print() {
// ...
}
func (s *struct2) Print() {
// ...
}
var result Printer
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
https://play.golang.org/p/W9r6UfeQSCz
You needs match "generical interface" with structs. See this:
//my struct1
type MyStruct1 struct {
ID int
}
//my struct2
type MyStruct2 struct {
ID int
}
//my interface
type MyStructGenerical interface {
Print()
}
//method (match with MyStructGenerical)
func (m1 MyStruct1) Print() {
println(m1.ID)
}
//method (match with MyStructGenerical)
func (m2 MyStruct2) Print() {
println(m2.ID)
}
In this way you can does assertions between structs and generical interface. See this:
//here result is an generical interface
var result MyStructGenerical = nil
//checkin
switch structPickingString {
case "struct1":
tmp := new(MyStruct1)
result = tmp
case "struct2":
tmp := new(MyStruct2)
result = tmp
}
The result is:
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(body()).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
tmp := result.(*MyStruct1)
tmp.Print()
// or
result.Print()
Run in: https://play.golang.org/p/nHrJnImsqNN
I think you want to use an interface like
type ResultIface interface {
Print()
}
Then you can do
var result ResultIface
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
As long as your struct1 and struct2 fulfills the ResultIface by having a Print function it will print

How to make parameter of function is a "generic" struct

For example, I want to write a method like this:
func parseData(rawData []json.RawMessage) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
// this is an custom struct
command := UserCommand{}
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
The problem of this code is: If I don't want to parse UserCommand but any other such as ProductCommand, I must write the same code, only different at line: command := UserCommand{}. So my question is: how can I generic this code.
I have tried this solution but it doesn't work:
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
command := class
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
// then I call this method
parseData(data, UserCommand{})
But it doesn't work. It return array of map[string]interface{} How can I fix this.
Edit:
Here is some my defined struct
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quanlity int
}
// I want to be able to call this method
parseData(data, UserCommand{})
It is possible to support this style of "generic" signature by using Go's reflect package.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quantity int
}
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var parsed []interface{}
for _, elem := range rawData {
// Create a pointer to a new zero value of the same type as `class`.
command := reflect.New(reflect.TypeOf(class))
// Pass a pointer to the new value to `json.Unmarshal`.
json.Unmarshal(elem, command.Interface())
// Insert the pointed-to new value into the output slice.
parsed = append(parsed, command.Elem().Interface())
}
return parsed
}
func main() {
data := []json.RawMessage{
json.RawMessage(`{"User":"u1","Info":"i1"}`),
json.RawMessage(`{"User":"u2","Info":"i2"}`),
}
parsed := parseData(data, UserCommand{})
fmt.Printf("%#v\n", parsed)
data = []json.RawMessage{
json.RawMessage(`{"Name":"n1","Quantity":1}`),
json.RawMessage(`{"Name":"n2","Quantity":2}`),
}
parsed = parseData(data, ProductCommand{})
fmt.Printf("%#v\n", parsed)
}
The output shows that the first parseData call has parsed two UserCommand structs and the second call has parsed two ProductCommand structs.
[]interface {}{main.UserCommand{User:"u1", Info:"i1"}, main.UserCommand{User:"u2", Info:"i2"}}
[]interface {}{main.ProductCommand{Name:"n1", Quantity:1}, main.ProductCommand{Name:"n2", Quantity:2}}

Resources