Is it possible to use mass assignment in golang? - go

I have my struct like this:
type User struct {
Id uint64
Email string
}
And I know, that I can declare it like this:
user := User{
Id: 1,
Email: "test#example.com",
}
And I can update it like this:
user.Id = 2
user.Email = "test1#example.com"
Is it possible to use similar construction like for creating but for updating struct?

No, there really isn't an equivalent multi-property setter.
EDIT:
Possibly with reflection you could do something like:
updates := user := User{
Email: "newemail#example.com",
}
//via reflection (pseudo code)
for each field in target object {
if updates.field is not zero value{
set target.field = updates.field
}
}
The reflection part could be factored into a function updateFields(dst, src interface{}), but I would generally say the complexity is not worth the savings. Just have a few lines of setting fields one by one.

It's not the same, but you can use the multivalue return functionality to set them in one line.
https://play.golang.org/p/SGuOhdJieW
package main
import (
"fmt"
)
type User struct {
Id uint64
Email string
Name string
}
func main() {
user := User{
Id: 1,
Email: "test#example.com",
Name: "Peter",
}
fmt.Println(user)
user.Id, user.Email = 2, "also-test#example.com"
fmt.Println(user) // user.Name = "Peter"
}

Do you mean like this?
package main
import (
"fmt"
)
type User struct {
Id uint64
Email string
}
func main() {
user := User{
Id: 1,
Email: "test#example.com",
}
fmt.Println(user)
user = User{
Id: 2,
Email: "also-test#example.com",
}
fmt.Println(user)
}
https://play.golang.org/p/5-ME3p_JaV

Related

How to clear struct values except certain fields with struct's method

I have a struct. I want to clear all fields except some public fields, e.g. Name, Gender, how to implement the function through method?
In my real code, I have many fields in the struct, so reset those sensitive fields manually is not my option.
type Agent struct {
Name string
Gender string
Secret1 string
Secret2 string
}
func (a *Agent) HideSecret() {
fmt.Println("Hidding secret...")
new := &Agent{
Name: a.Name,
Gender: a.Gender,
}
a = new
}
I tried a few combination of * and &, but it seems not working...
Please help.
James := Agent{
Name: "James Bond",
Gender: "M",
Secret1: "1234",
Secret2: "abcd",
}
fmt.Printf("[Before] Secret: %s, %s\n", James.Secret1, James.Secret2)
James.HideSecret()
fmt.Printf("[After] Secret: %s, %s\n", James.Secret1, James.Secret2) // not working
The golang playground is here: https://go.dev/play/p/ukJf2Fa0fPI
The receiver is a pointer. You have to update the object that the pointer points to:
func (a *Agent) HideSecret() {
fmt.Println("Hidding secret...")
cleaned := Agent{
Name: a.Name,
Gender: a.Gender,
}
*a=cleaned
}
If you want to just clear the fields, this is an easy solution. it saves some memory
func (a *Agent) HideSecret() {
fmt.Println("Hidding secret...")
a.Secret1 = ""
a.Secret2 = ""
}

Unmarshal DynamoDBAttributeValues into a struct with different attributes

I read some data from dynamodb. This is what I get
{
Item: {
rating: {
N: "6"
},
pk: {
S: "artist-1"
},
gender: {
S: "woman"
},
sk: {
S: "Alexandra A"
}
}
}
Now I have a struct which looks like this:
type Artist struct {
ArtistID string `json:"id"`
Gender string `json:"gender"`
Name string `json:"name"`
Rating float64 `json:"rating"`
}
Now I do
artist := model.Artist{}
err = dynamodbattribute.UnmarshalMap(result.Item, &artist)
Now I can access e.g. gender with artist.gender. So this is fine, but I can't do this for ArtistId because it's called pk in my dynamodb and I use 'id' for my struct. What is a clean way to solve this? I don't want to replace my 'id' with 'pk' in the struct.
UnmarshalMap() didn't support any given tag Unmarshal. If you don't want to change 'id' with 'pk' in the struct then you have to set value of pk in id key before UnmarshalMap in map manually.
func UnmarshalMap(m map[string]*dynamodb.AttributeValue, out interface{}) error {
m["id"] = m["pk"]
dynamodbattribute.UnmarshalMap(m, out)
}
Tt's better to create a own generic Unmarshal function for this special case tweak and then call UnmarshalMap() inside.
artist := model.Artist{}
err = UnmarshalMap(result.Item, &artist)

How to print the slice of pointers to get the values instead of their address without iteration at go?

This kind output required for debugging purpose.
To get actual value of slice of pointers, every time a iteration is getting required.
Is there any way, we can directly have the value instead of the address of each item present at slice using simple fmt.printf()?
Here is a code snippet :
https://play.golang.org/p/bQ5vWTlKZmV
package main
import (
"fmt"
)
type user struct {
userID int
name string
email string
}
func main() {
var users []*user
addUsers(users)
}
func addUsers(users []*user) {
users = append(users, &user{userID: 1, name: "cooluser1", email: "cool.user1#gmail.com"})
users = append(users, &user{userID: 2, name: "cooluser2", email: "cool.user2#gmail.com"})
printUsers(users)
printEachUser(users)
}
func printUsers(users []*user) {
fmt.Printf("users at slice %v \n", users)
}
func printEachUser(users []*user) {
for index, u := range users {
fmt.Printf("user at user[%d] is : %v \n", index, *u)
}
}
At above code, if I am printing the slice directly by fmt.printf , I get only the address of the values instead of actual value itself.
output : users at slice [0x442260 0x442280]
To read to the values always, i have to call func like printEachUser to iterate the slice and get the appropriate value .
output:
user at user[0] is : {1 cooluser1 cool.user1#gmail.com}
user at user[1] is : {2 cooluser2 cool.user2#gmail.com}
Is there any way, we can read the values inside the slice of pointers using fmt.printf and get value directly like below ?
users at slice [&{1 cooluser1 cool.user1#gmail.com} , &{2 cooluser2 cool.user2#gmail.com}]
This kind output required for debugging purpose.
Is there any way, we can read the values inside the slice of pointers
using fmt.Printf and get value directly like below ?
users []*user
fmt.Printf("users at slice %v \n", users)
users at slice [&{1 cooluser1 cool.user1#gmail.com}, &{2 cooluser2 cool.user2#gmail.com}]
Package fmt
import "fmt"
type Stringer
Stringer is implemented by any value that has a String method, which
defines the “native” format for that value. The String method is used
to print values passed as an operand to any format that accepts a
string or to an unformatted printer such as Print.
type Stringer interface {
String() string
}
For example,
package main
import (
"fmt"
)
type user struct {
userID int
name string
email string
}
type users []*user
func (users users) String() string {
s := "["
for i, user := range users {
if i > 0 {
s += ", "
}
s += fmt.Sprintf("%v", user)
}
return s + "]"
}
func addUsers(users users) {
users = append(users, &user{userID: 1, name: "cooluser1", email: "cool.user1#gmail.com"})
users = append(users, &user{userID: 2, name: "cooluser2", email: "cool.user2#gmail.com"})
fmt.Printf("users at slice %v \n", users)
}
func main() {
var users users
addUsers(users)
}
Playground: https://play.golang.org/p/vDmdiKQOpqD
Output:
users at slice [&{1 cooluser1 cool.user1#gmail.com}, &{2 cooluser2 cool.user2#gmail.com}]
Code : https://play.golang.org/p/rBzVZlovmEc
Output :
users at slice [{1 cooluser1 cool.user1#gmail.com} {2 cooluser2
cool.user2#gmail.com}]
Using stringers you can achive it.
Refer: https://golang.org/pkg/fmt/#Stringer
package main
import (
"fmt"
)
type user struct {
userID int
name string
email string
}
func (t user) String() string {
return fmt.Sprintf("{%v %v %v}", t.userID, t.name, t.email)
}
func main() {
var users []*user
addUsers(users)
}
func addUsers(users []*user) {
users = append(users, &user{userID: 1, name: "cooluser1", email: "cool.user1#gmail.com"})
users = append(users, &user{userID: 2, name: "cooluser2", email: "cool.user2#gmail.com"})
printUsers(users)
}
func printUsers(users []*user) {
fmt.Printf("users at slice %v \n", users)
}
You need not apply stringer to users i.e []*users instead if you do it just for a single user it'll work fine. Also it reduces down the string operations you need to do manually making your code elegant.
You can use spew
go get -u github.com/davecgh/go-spew/spew
func spewDump(users []*user) {
_, err := spew.Printf("%v", users)
if err != nil {
fmt.Println("error while spew print", err)
}
}
Output:
[<*>{1 cooluser1 cool.user1#gmail.com} <*>{2 cooluser2 cool.user2#gmail.com}]

Copy one struct to another where structs have same members and different types

I have two struct having the same members, I want to copy one struct to another, see the pseudo code below:
type Common struct {
Gender int
From string
To string
}
type Foo struct {
Id string
Name string
Extra Common
}
type Bar struct {
Id string
Name string
Extra Common
}
Then I have foo of struct Foo, and bar of struct Bar, Is there any way to copy bar from foo?
Use a conversion to change the type. The following code uses a conversion to copy a value of type Foo to a value of type Bar:
foo := Foo{Id: "123", Name: "Joe"}
bar := Bar(foo)
playground example
The conversion only works when the underlying types are identical except for struct tags.
https://github.com/jinzhu/copier (same author of gorm) is also quite a good one, I have nested structs and all I do is:
copier.Copy(&employees, &user)
works great
If you would like to copy or clone to a different struct, I would suggest using deepcopier
It provides nice features like skipping, custom mapping, and forcing. below is an example from github:
Install:
go get -u github.com/ulule/deepcopier
Example:
package main
import (
"fmt"
"github.com/ulule/deepcopier"
)
// Model
type User struct {
// Basic string field
Name string
// Deepcopier supports https://golang.org/pkg/database/sql/driver/#Valuer
Email sql.NullString
}
func (u *User) MethodThatTakesContext(ctx map[string]interface{}) string {
// do whatever you want
return "hello from this method"
}
// Resource
type UserResource struct {
//copy from field "Name"
DisplayName string `deepcopier:"field:Name"`
//this will be skipped in copy
SkipMe string `deepcopier:"skip"`
//this should call method named MethodThatTakesContext
MethodThatTakesContext string `deepcopier:"context"`
Email string `deepcopier:"force"`
}
func main() {
user := &User{
Name: "gilles",
Email: sql.NullString{
Valid: true,
String: "gilles#example.com",
},
}
resource := &UserResource{}
deepcopier.Copy(user).To(resource)
//copied from User's Name field
fmt.Println(resource.DisplayName)//output: gilles
fmt.Println(resource.Email) //output: gilles#example.com
fmt.Println(resource.MethodThatTakesContext) //output: hello from this method
}
Also, some other way you could achieve this is by encoding the source object to JSON and then decode it back to the destination object.
This is another possible answer
type Common struct {
Gender int
From string
To string
}
type Foo struct {
Id string
Name string
Extra Common
}
type Bar struct {
Id string
Name string
Extra Common
}
foo:=Foo{
Id:"123",
Name:"damitha",
Extra: struct {
Gender int
From string
To string
}{Gender:1 , From:"xx", To:"yy" },
}
bar:=*(*Bar)(unsafe.Pointer(&foo))
fmt.Printf("%+v\n",bar)
If you would like to copy or clone to a different struct, I would suggest using deepcopier
It provides nice features like skipping, custom mapping, and forcing.
You can achieve nested struct copy following way.
Install:
go get -u github.com/ulule/deepcopier
Example:
package main
import (
"fmt"
"strconv"
"github.com/ulule/deepcopier"
)
//FieldStruct - Field Struct
type FieldStruct struct {
Name string `deepcopier:"field:TargetName"`
Type string `deepcopier:"field:TargetType"`
}
//SourceStruct - Source Struct
type SourceStruct struct {
Name string `deepcopier:"field:TargetName"`
Age int `deepcopier:"field:TargetAge"`
StringArray []string `deepcopier:"field:TargetStringArray"`
StringToInt string `deepcopier:"context"`
Field FieldStruct
Fields []FieldStruct
}
//TargetFieldStruct - Field Struct
type TargetFieldStruct struct {
TargetName string
TargetType string
}
//TargetStruct - Target Struct
type TargetStruct struct {
TargetName string
TargetAge int
TargetStringArray []string
TargetInt int
TargetField TargetFieldStruct
TargetFields []TargetFieldStruct
}
//write methods
//TargetInt - StringToInt
func (s *SourceStruct) TargetInt() int {
i, _ := strconv.Atoi(s.StringToInt)
return i
}
func main() {
s := &SourceStruct{
Name: "Name",
Age: 12,
StringArray: []string{"1", "2"},
StringToInt: "123",
Field: FieldStruct{
Name: "Field",
Type: "String",
},
Fields: []FieldStruct{
FieldStruct{
Name: "Field1",
Type: "String1",
},
FieldStruct{
Name: "Field2",
Type: "String2",
},
},
}
t := &TargetStruct{}
//coping data into inner struct
deepcopier.Copy(&t.TargetField).From(&s.Field)
// copied array of Struct
for i := range s.Fields {
// init a struct
t.TargetFields = append(t.TargetFields, TargetFieldStruct{})
// coping the data
deepcopier.Copy(&t.TargetFields[i]).From(&s.Fields[i])
}
//Top level copy
deepcopier.Copy(t).From(s)
fmt.Println(t)
}
Output:
&{Name 12 [1 2] 123 {Field String} [{Field1 String1} {Field2 String2}]}

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