Copy slice with reflect and unsafe package - go

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

Related

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,
},
}
}

get slice of specified field from slice of structs

I have a slice of structs that contains 10 cars like:
type struct car {
engine
window
wheel
}
so the slice cars contains 10 car struct.
I would like to know if a function exist such as:
var engines string[] = cars.Getfield("engine") // engines will contain 10 engines names
There's no library function for this.
You can implement manually using reflect package
Example:
type Cars []Car
func (cars Cars) getFieldString(field string) []string {
var data []string
for _, car := range cars {
r := reflect.ValueOf(car)
f := reflect.Indirect(r).FieldByName(field)
data = append(data, f.String())
}
return data
}
Code in Playground here
There's no generics in Go (not until 2.0 at least), so there's not a lot of helper functions out there.
If you need to use this function often, you can implement it as a method for type engines.
Answering #RodolfoAP question under #Joe-Akanesuvan answer (not enough rep to post as a comment):
Even though generics are a part of Go now, there're no functional programming libs in std, I used 1st one I found on awesome-go, so this is probably isn't production-ready code, but that's generally what it would look like:
package main
import (
"fmt"
fp "github.com/repeale/fp-go"
)
type data struct {
field string
anotherField int
}
func main() {
fmt.Printf(
"result: %+v",
fp.Map(func(d data) string { return d.field })(
[]data{
{
field: "apple",
anotherField: 1,
},
{
field: "orange",
anotherField: 2,
},
{
field: "banana",
anotherField: 3,
},
},
),
)
}
Code output:
result: [apple orange banana]
Code in playground here

Save Struct to binary file

I'm trying same my nested struct to binary files.
In future there will be a lots of "Rooms" records so serialized struct in binary file is the best approach I think.
package main
import (
"bytes"
"encoding/binary"
"log"
"time"
)
type House struct {
ID int
Floors int
Rooms []Room
}
type Room struct {
Width int
Height int
Description string
CreatedAt time.Time
}
func main() {
var house House = House{
ID: 1,
Floors: 3,
}
house.Rooms = append(house.Rooms, Room{Width: 20, Height: 30, CreatedAt: time.Now(), Description: "This is description"})
house.Rooms = append(house.Rooms, Room{Width: 14, Height: 21, CreatedAt: time.Now(), Description: "This is other description"})
house.Rooms = append(house.Rooms, Room{Width: 12, Height: 43, CreatedAt: time.Now(), Description: "This is other desc"})
log.Println(house)
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, house)
if err != nil {
log.Println(err)
}
}
But I have error:
- Binary.Write: invalid type main.House
Could someone help me because I cant find solution.
According to the binary.Write documentation:
Data must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
Your House structure is not a fixed size value.
You might consider writing/reading House and Room separately. The House you use to store house structs must not contain a slice, so you can declare another House struct that you use to read/write from your file.
Instead of a binary file you can store your objects as JSON, and then you wouldn't need to deal with this problem.

print a struct within a struct golang

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);
}

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