cannot assign to struct field in map - go

I would like to set a default value and if this is not set in a structure, I would like to set it. It's a bit confusing, but please take a look to the (simplified) code:
package main
import "log"
type Something struct {
A string
B map[string]Type
C Epyt
}
type Type struct {
A Epyt
B string
}
type Epyt struct {
A string
B string
}
func main() {
var a Something
a.A = "Test A (Something)"
// var a.B["one"] Type
a.B["one"].A.A = a.B["one"].A.A
a.B["one"].A.A = "Test A ([one]Type.Epyt)"
a.B["two"].A.A = "Test A ([two]Type.Epyt)"
a.C.A = "Test A (Epyt)"
a.C.B = "Test B (Epyt)"
for i := range a.B {
if a.B[i].A.B == "" {
a.B[i].A.B = a.C.B
}
}
log.Printf("%+v", a)
}
I'm working with viper to unmarshal a config file, therefor I'm not able to use pointers (or am I wrong?).
The error I'm getting, is cannot assign to struct field *** in map.
I found that this is an old bug in go lang still not corrected.
I've read Why do I get a "cannot assign" error when setting value to a struct as a value in a map? and Golang: I have a map of structs. Why can't I directly modify a field in a struct value? but as I said, I can't operate with pointers (or?), therefor please don't mark this as duplicate as it is not!
If someone has an idea on how to do this right, please help!

I played around a bit and got it working! I thought there is an error, because if I use printf over the whole structure, I get the address instead of the value back. Sorry for the inconvenience!
As #mkopriva told me, I tried to use pointers. After some errors I got it to work so far! Sorted out this brain bug ;)
Thank you again!

You haven't initialised the map.
Try
var a Something
a.B = make(map[string]Type)
Also, none of the structures you're referring to exist.
Ie, instead of:
a.B["one"].A.A = ...
You should do:
a.B["one"] = Type{
A: Epyt{
A: "test",
B: "foo",
},
B: "something",
}

Related

cannot assign to struct field / assignment to entry in nil map [duplicate]

This question already has answers here:
How to update map values in Go
(3 answers)
Closed 4 years ago.
Does anyone have any idea why this doesn't work?
I've been racking my brain for about 4 hours now. I cannot make it work.
In my opinion, I'm trying to do the same thing twice. I'm making a map of a field in a struct, and then I'm doing it again on another field in a different struct.
If I try to make the map. It fails with: "cannot assign to struct field. Bet.Events["one"].Runners in map".
So if I remove the make, and just try to assign it, I get: "assignment to entry in nil map"
https://play.golang.org/p/kRcXoyO-8FN
var Bet BetStruct
type BetStruct struct {
Events map[string]Event
}
type Event struct {
Name string
Runners map[int]Runner
}
type Runner struct {
Name string
}
func main(){
// == THIS WORKS FINE ==
Bet.Events = make(map[string]Event)
Bet.Events["one"] = Event{
Name: "first",
}
Bet.Events["two"] = Event{
Name: "second",
}
// == THIS DOES NOT WORK ==
Bet.Events["one"].Runners = make(map[int]Runner)
Bet.Events["one"].Runners[0] = Runner{
Name: "Jeff",
}
}
The default value for map is nil (before or without make(map))
You set a default value for Runners in this code:
Bet.Events["one"] = Event{
Name: "first",
}
So, Runners wasn't defined clearly and was setting to nil.
Structs are mutable except map elements. (From below comment of #icza: "Structs are modifiable. It's values stored in map that are not modifiable, because values stored in a map are not addressable.")
You cannot reinitialize map element Bet.Events["one"] because is not addressable.
You need to do:
Bet.Events["one"] = Event{
Name: "first",
Runners: make(map[int]Runner),
}
Thanks #icza for marking errors in the text

How to assign field name of struct from variable

I am new to golang and migrating from php to golang.
I am trying to do something like below stuff, where I want field name age to get assigned from variable test. Is this possible in golang?
In php, we have provision like $$test, looking something similar in golang as well.
package main
import "fmt"
// This `person` struct type has `name` and `age` fields.
type person struct {
name string
age int
}
func main() {
var test = "age"
fmt.Println(person{name: "Alice",test: 30})
}
This is just sample code replicating my use case.
You have three options, in rough order of preference:
1) An if/switch statement:
var p = &person{}
if key == "age" {
p.age = value
}
2) Use a map instead of a struct:
var p = map[string]interface{}
p[key] = value
3) Use reflection. See this question for details, but generally you should avoid reflection. It's slow, and non-idiomatic, and it only works with exported fields (your example uses un-exported fields, so as written, is not a candidate for reflection anyway).

Cannot use as type in assignment in go

when I compile my code, I get the following error message, not sure why it happens. Can someone help me point why? Thank you in advance.
cannot use px.InitializePaxosInstance(val) (type PaxosInstance) as
type *PaxosInstance in assignment
type Paxos struct {
instance map[int]*PaxosInstance
}
type PaxosInstance struct {
value interface{}
decided bool
}
func (px *Paxos) InitializePaxosInstance(val interface{}) PaxosInstance {
return PaxosInstance {decided:false, value: val}
}
func (px *Paxos) PartAProcess(seq int, val interface{}) error {
px.instance[seq] = px.InitializePaxosInstance(val)
return nil
}
Your map is expecting a pointer to a PaxosInstance (*PaxosInstance), but you are passing a struct value to it. Change your Initialize function to return a pointer.
func (px *Paxos) InitializePaxosInstance(val interface{}) *PaxosInstance {
return &PaxosInstance {decided:false, value: val}
}
Now it returns a pointer. You can take the pointer of a variable using & and, should you need the struct value itself, dereference it again with *.
After a line like
x := &PaxosInstance{}
or
p := PaxosInstance{}
x := &p
the value type of x is *PaxosInstance. And if you ever need to, you can dereference it back into a PaxosInstance struct value with
p = *x
You usually do not want to pass structs around as actual values, because Go is pass-by-value, which means it will copy the whole thing. Using struct values with maps and slices often results in logic errors because a copy is made should you iterate them or otherwise reference them except via index. It depends on your use-case, but your identifier Instance would infer that you would want to avoid duplications and such logic errors.
As for reading the compiler errors, you can see what it was telling you. The type PaxosInstance and type *PaxosInstance are not the same.
The instance field within the Paxos struct is a map of integer keys to pointers to PaxosInstance structs.
When you call:
px.instance[seq] = px.InitializePaxosInstance(val)
You're attempting to assign a concrete (not pointer) PaxosInstance struct into an element of px.instance, which are pointers.
You can alleviate this by returning a pointer to a PaxosInstance in InitializePaxosInstance, like so:
func (px *Paxos) InitializePaxosInstance(val interface{}) *PaxosInstance {
return &PaxosInstance{decided: false, value: val}
}
or you could modify the instance field within the Paxos struct to not be a map of pointers:
type Paxos struct {
instance map[int]PaxosInstance
}
Which option you choose is up to your use case.
For anyone else pulling their hair out: check your imports.
Not sure when it started happening, but my Visual Studio Code + gopls setup will occasionally insert an import line that references my vendored dependencies path instead of the original import path. I usually won't catch this until I start polishing code for release, or an error like this one pops up.
In my case this caused two otherwise identical types to not compare equally. Once I fixed my imports this resolved the error.

Does declaring a variable as optional incurs any additional cost in swift?

As per my boss, if I am initialising a variable in init, I should not declare it as optional and he thinks book on swift says that.
Now I haven't came across the same statement anywhere hence wondering is he correct or not.
OverView:
As I come from an Object oriented Objective C programming background, it is my hobby to declare the variables to their initial values as soon as I declare it (most of the Stanford university videos on Objective C said the same thing) Now objective C used to allow a nil value to the variables and if I want to assign the same nil value as initial value to some variable in swift I need to declare it as optional, I know that if I declare a variable and won't initialise it in init I'll get error from compiler,
struct TestStruct {
var name : String? = nil
var age : Int
init(){
name = "Sandeep"
age = 26
}
func printInfo(){
print(self.name! + "\(self.age)")
}
}
In the above code I have name declared as optional and set nil as default value where as age is defined as Int and initialised to proper value in init.
Question
Knowing that both work,
1.Is it wrong if I declare a variable as optional and then use ! or ? to access it even though I initialise it in init??
2.Is it true that swift specifies that I should not use ? or ! for the variables ? If yes is there any added computational cost to it ?
I know it is not a complicated question, I just wanna know what I am doing is anyway wrong?
Thanks in advance.
Assuming you don't want to reset your struct's name to nil ever, initialize the variables with the values you want them to have. It simplifies the code and expresses the idea of default values.
struct TestStruct {
var name = "Sandeep"
var age = 26
func printInfo(){
print(self.name + "\(self.age)")
}
}
Yes, your boss is correct but it depends on the requirements. If you are for sure that you would be having a name and age then you can avoid optionals for these two. If you are not sure about the values then you should use the optionals like for address or phone number etc. And also it is best practice to initialize with the parameters in the struct like below:
struct TestStruct {
var name : String?
var age : Int
init(name: String?, age: Int){
self.name = name
self.age = age
}
func printInfo(){
//Since your name property is an optional, you should check for a nil before using it.
print("Name:", (self.name ?? "") + "\nAge: \(self.age)")
}
}
let myStruct = TestStruct(name: "Sandeep", age: 26)
myStruct.printInfo()
//output
Name: Sandeep
Age: 26
let myStruct2 = TestStruct(name: nil, age: 18)
myStruct2.printInfo()
//output
Name: No Name provided
Age: 18

Is there a shortcut for assigning a variable to a pointer without creating the variable in a separate line first?

If I have a struct like this:
type Message struct {
Id int64
Message string
ReplyTo *int64
}
And then if I did create an instance of this struct like this:
var m Message
m.Id = 1
m.Message = "foo bar yo"
var replyTo = int64(64)
m.ReplyTo = &replyTo
Then it would work.
But I was wondering if there was a shortcut for the last step?
I tried doing something like:
m.ReplyTo = &int64{64}
But it did not work.
I don't think you can because the value is a primitive and attempting to do it in one shot like the below would be a syntax error. Its attempting to get an address of a value so it wouldn't be possible. At least I am not aware of a way where its possible.
someInt := &int64(10) // would not compile
The other alternative you have is to write a function to return a pointer to the primitive like the following:
func NewIntPointer(value int) *int {
return &value
}
A tricky way to get int pointer without create new variable.
someIntPtr := &[]int64{10}[0]

Resources