print a struct within a struct golang - go

I want to print a certain item within a struct, that is within a struct. Example:
Pretend I have made up a blueprint struct, and are making an new one
Pretend I have a myshapes struct, that contains a title, # of circles, and squares
car := blueprints{
dots: 5,
lines: 25,
shapes: []myshapes{
myshapes{
title:"car #1",
circles:5,
squares:7,
},
myshapes{
title:"car #2",
circles:2,
squares:14,
},
}
How do I print:
title:"car #1"
circles:5
squares:7
title:"car #2"
circles:2
squares:14

The example below shows how you'd print a particular field value of a struct:
type child struct {
name string
age int
}
type parent struct {
name string
age int
children child
}
func main() {
family := parent{
name: "John",
age: 40,
children: child{
name: "Johnny",
age: 10,
},
}
fmt.Println(family.children.name); // Prints "Johnny"
}
The code above would print "Johnny", which is a value of a struct within the parent struct. The code doesn't make much sense, however, because the field is children, yet there is only ever able to be one child as its value.
Let's leverage slices. Now that we know how to print a particular value, all we would need for your case is to loop over a slice and print the same way we did above.
We can do that like this:
type child struct {
name string
age int
}
type parent struct {
name string
age int
children []child
}
func main() {
family := parent{
name: "John",
age: 40,
children: []child{
child{
name: "Johnny",
age: 10,
},
child{
name: "Jenna",
age: 7,
},
},
}
for _, child := range family.children {
fmt.Println(child.name);
}
}
The above example will pront "Johnny" and "Jenna".
You can use a similar pattern to what I showed above in your own code to loop through your structs and print whatever value your heart desires :)
Keep in mind, there are many ways to loop through things. For example, all the following loops would print "Johnny" and "Jenna" from the examples above.
for _, child:= range family.children{ // Prints "Jonny" and "Jenna"
fmt.Println(child.name);
}
for i, _ := range family.children{ // Prints "Jonny" and "Jenna"
fmt.Println(family.children[i].name);
}
for i := 0; i < len(family.children); i++ { // Prints "Jonny" and "Jenna"
fmt.Println(family.children[i].name);
}

Related

Copy slice with reflect and unsafe package

I understand that usage of unsafe package of golang is unsafe, but I'm doing it only for education purposes.
The idea is to copy a slice field of structure, copy it with reflect package and unsafe.pointer, modify and replace the original slice with new one.
And it looks like new slice is created and has correct capacity/length, and it contains an instance of GoodForSale type. But all fields of that instance (Name, Price and Qnty) have wrong values. So I suppose that I'm doing something wrong with pointers and getting garbade data:
package main
import (
"fmt"
"reflect"
"unsafe"
)
type GoodForSale struct {
Name string
Price int
Qnty int
}
type Lead struct {
ID int
Name string
ContactName string
Budget int
Items []GoodForSale
DateTime string
Status int
Win bool
}
func main() {
lead := &Lead{
ID: 41,
Name: "Phone",
ContactName: "John Smith",
Budget: 30000,
Items: []GoodForSale{
{
Name: "Iphone 6",
Price: 100,
Qnty: 3,
},
},
DateTime: "12.08.2020 11:23:10",
Status: 150,
Win: false,
}
//Change Items
pt1 := unsafe.Pointer(&lead.Items)
copyItems := &reflect.SliceHeader{
Data: uintptr(pt1),
Cap: cap(lead.Items),
Len: len(lead.Items),
}
items := *(*[]GoodForSale)(unsafe.Pointer(copyItems))
fmt.Println(items[0].Name)
}
It looks like I'm missing something about how pointers work here. But how can I make this idea work correct?
Here is a go playground url: https://play.golang.org/p/SKtJWe9RVEU
The problem is here:
pt1 := unsafe.Pointer(&lead.Items) // pointer the slice, a.k.a "slice header"
copyItems := &reflect.SliceHeader{
Data: uintptr(pt1), // trying to use it as pointer to the actual data.
"Data" needs a pointer to where the actual data starts, but you're giving a pointer to the slice itself.
This is the correct way to get a pointer to the slice's data:
pt1 := unsafe.Pointer(&lead.Items[0]) // pointer to the first element referenced by the slice
Note that this will panic if len(lead.Items) == 0. In that case, you should use unsafe.Pointer(nil).
You could also get the data pointer (along with len and cap) from the original slice by casting it as a reflect.SliceHeader like this:
copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))
items := *(*[]GoodForSale)(unsafe.Pointer(&copyHeader))
but at this point we've essentially just made a convoluted and "unsafe" version of this:
items := lead.Items
Based on the inputs from Hymns For Disco modifying your code by providing the element of slice instead of whole slice to the pointer.
package main
import (
"fmt"
"reflect"
"unsafe"
)
type GoodForSale struct {
Name string
Price int
Qnty int
}
type Lead struct {
ID int
Name string
ContactName string
Budget int
Items []GoodForSale
DateTime string
Status int
Win bool
}
func main() {
lead := &Lead{
ID: 41,
Name: "Phone",
ContactName: "John Smith",
Budget: 30000,
Items: []GoodForSale{
{
Name: "Iphone 6",
Price: 100,
Qnty: 3,
},
},
DateTime: "12.08.2020 11:23:10",
Status: 150,
Win: false,
}
pt1 := unsafe.Pointer(&lead.Items[0])
copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))
items := *(*[]GoodForSale)(unsafe.Pointer(&copyHeader))
//items := *(*[]GoodForSale)(unsafe.Pointer(copyItems))
fmt.Println(pt1)
fmt.Println(items[0].Name)
}
Output:
0xc00000c080
Iphone 6

Reduce a large struct into a small one?

Say I make a call to an api, which returns a slice of structs, each with a load of methods and fields, but I only want to use one field per each element of the returned values. How can I do this?
For instance, I call out to an API, and it returns a slice of x elements, each which has 4 values and 13method, but I only want 1 value and 0 methods (the slice of fetus structs). How can I marshall this into my own struct? eg:
func GETApi() []fetus {
//doGet() returns a slice of persons, which are described below
a := doGet() // this gets many detailed persons as a slice, but I just want them as a slice of fetus
/*
type person struct {
id:
age:
height:
width:
}
func (a *person) GetHeight() int { ... }
func (a *person) GetWidth() int { ... }
func (a *person) GetLaught() int { ... }
// I want to return a slice of these ([]fetus)
type fetus struct {
id:
}
var f fetus
f := a // how can I condense said slice of persons into a slice of fetus
return f
*/
Perhaps something like this?
package main
type Person struct {
Id string
Name string
Age string
LotsOfOtherFields string
}
type Fetus struct {
Id string
}
func main() {
persons := []Person{
{Id: "a", Name: "John"},
{Id: "b", Name: "Steve"},
{Id: "c", Name: "Fred"},
}
fetuses := make([]Fetus, len(persons))
for i, p := range persons {
// Create a new fetus struct and pluck the ID from the person struct
fetuses[i] = Fetus{Id: p.Id}
}
}

How to use pointers or indexing on a range to resolve rangeValCopy gocritic message

I'm getting the following output when running https://golangci-lint.run/:
rangeValCopy: each iteration copies 128 bytes (consider pointers or indexing) (gocritic)
for _, v := range products {
Here is a cut down version of the code I am running:
package main
import (
"fmt"
"encoding/json"
)
type Application struct {
ProductData []ProductDatum
}
type ProductDatum struct {
Name string
ProductBrand string
ProductType string
}
type Item struct {
ProductBrand string
ProductName string
ProductType string
}
func main() {
appl := Application{
ProductData: []ProductDatum{
{
Name: "Baz",
ProductBrand: "Foo",
ProductType: "Bar",
},
},
}
products := appl.ProductData
var orderLinesItem []Item
for _, v := range products {
item := []Item{
{
ProductBrand: v.ProductBrand,
ProductName: v.Name,
ProductType: v.ProductType,
},
}
orderLinesItem = append(orderLinesItem, item...)
}
body, _ := json.Marshal(orderLinesItem)
fmt.Println(string(body))
}
Here it is in go playground.
What does this output mean and how can I do what it's asking? I tried to use a pointer on each item but that didn't seem to make a difference.
What the linter is trying to tell you is that, by using range the way you are using it, each time you get a new element v it is not returning an element directly from the collection, rather it's a new copy of that element. The linter suggest two approaches:
Either change the slice to be a slice of pointers to struct, that way each iteration of the for loop will get a reference to an element instead of a full struct copy
var products []*ProductDatum
//fill products slice
var orderLinesItem []Item
for _, v := range products{
//here v is a pointer instead of a full copy of a struct.
//Go dereferences the pointer automatically therefore you don't have to use *v
item := []Item{
{
ProductBrand: v.ProductBrand,
ProductName: v.Name,
ProductType: v.ProductType,
},
}
}
The other suggestion from the linter is using the index value that the range returns on each iteration
for i := range products{
item := []Item{
{
//access elements by index directly
ProductBrand: products[i].ProductBrand,
ProductName: products[i].Name,
ProductType: products[i].ProductType,
},
}
}

Update the fields of one struct to another struct

I have a struct:
type Person struct {
FirstName string
LastName int
Age int
HairColor string
EyeColor string
Height string
}
And I have a function that takes in 2 parameters, and updates the first Person's fields to the second person's fields:
func updateFields(personA *Person, personB Person) {
personA.FirstName = personB.FirstName
personA.LastName = personB.LastName
// Don't want to change Age.
personA.HairColor = personB.HairColor
personA.EyeColor = personB.EyeColor
personA.Height = personB.Height
}
Instead of hard-coding the values to change, how can I loop through the fields and update the first Person to have the same values as the second Person except for the "Age" field?
You can just copy the entire personB into personA and make a backup of the Age field before:
func updateFields(personA *Person, personB Person) {
tempAge := personA.Age
*personA = personB
personA.Age = tempAge
}
For trivially copying every field, you can simply do something like *personA = personB. If you need to "not copy" just one specific field (same field every time), you could probably just save that field's value in a separate variable, copy everything with *personA = personB, and then copy the value back. But this is only useful for very specific situations. It wouldn't allow for example to have a dynamic set of fields not to copy.
If you want to do it with more flexibility, you can do it using reflection, sample code below.
Notice that there may be a few limitations. Notably, you can only set exported fields. Also, if you don't test for these limitations and accidentally try to set a field that's not settable, or with a value of type that's not assignable to that field, etc, the reflect package will happily panic. So it's wise to add a lot of checks before you actually .Set(...) the field.
import (
"fmt"
"reflect"
)
type Person struct {
FirstName string
LastName int
Age int
HairColor string
EyeColor string
Height string
}
func updateFields(personA *Person, personB Person) {
// .Elem() called to dereference the pointer
aVal := reflect.ValueOf(personA).Elem()
aTyp := aVal.Type()
// no .Elem() called here because it's not a pointer
bVal := reflect.ValueOf(personB)
for i := 0; i < aVal.NumField(); i++ {
// skip the "Age" field:
if aTyp.Field(i).Name == "Age" {
continue
}
// you might want to add some checks here,
// eg stuff like .CanSet(), to avoid panics
aVal.Field(i).Set(bVal.Field(i))
}
}
func main() {
b := Person{
FirstName: "Bruno",
LastName: 1,
Age: 2,
HairColor: "hello",
EyeColor: "world",
Height: "tall",
}
a := Person{}
fmt.Println(a)
updateFields(&a, b)
fmt.Println(a)
}

How to initialize inherited object's fields

I must be missing something. I cannot initialize an object's inherited fields without directly accessing them.
My goal is trying to keep it simple.
package main
type Page struct {
Title string
}
type Article struct {
Page
Id int
}
func main() {
// this generates a build error:
// "invalid field name Title in struct initializer"
//
p := &Article{
Title: "Welcome!",
Id: 2,
}
// this generates a build error:
// "invalid field name Page.Title in struct initializer"
//
p := &Article{
Page.Title: "Welcome!",
Id: 2,
}
// this works, but is verbose... trying to avoid this
//
p := &Article{
Id: 2,
}
p.Title = "Welcome!"
// as well as this, since the above was just a shortcut
//
p := &Article{
Id: 2,
}
p.Page.Title = "Welcome!"
}
Thanks in advance.
In Go, these fields from embedded structs are called promoted fields.
The Go Specification states (my emphasis):
Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.
This is how you can solve it:
p := &Article{
Page: Page{"Welcome!"},
Id: 2,
}
You have to init like this:
p := &Article{
Page: Page{
Title: "Welcome!",
},
Id: 2,
}
PlayGround: http://play.golang.org/p/CEUahBLwCT
package main
import "fmt"
type Page struct {
Title string
}
type Article struct {
Page
Id int
}
func main() {
// this generates a build error:
// "invalid field name Title in struct initializer"
//
p := &Article{
Page: Page{
Title: "Welcome!",
},
Id: 2,
}
fmt.Printf("%#v \n", p)
}

Resources