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

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

Related

Accessing a strut from another package [duplicate]

This question already has answers here:
Private fields and methods for a struct
(6 answers)
Closed 6 months ago.
I have the following modules
package apack
type Something structs{
a string
b string
}
var FullList []Something
func Complete() []Something {
FullList = append(FullList, Something{
a: 'first',
b: 'second'
})
return FullList
}
Now the following main
import "something/apack"
func main() {
re = apack.Complete()
for _,s := range re {
s1 := apack.Something(s)
fmt.Println(s1)
}
}
when I run it I get the following:
{first second}
but if I do something like
fmt.Println(s1.a)
I get the following error:
./main.go:70:19: s1.a undefined (type apack.Something has no field or method a)
Is it possible to be able to access structs from another package?
I think map should work, just unsure how for this case.
Thanks
Yes it's possible, just make the fields of your struct (that you want to access from the other package) exported:
type Something struct {
A string
b string
}
Then you can access all the exported fields from the struct so fmt.Println(s1.A) in the other package code will now work, but fmt.Println(s1.b) will not work as b is still an unexported field.
Also, here is a very simple lesson from A Tour of Go (which I also recommend as a whole) about exported names.

Why it is not allowed to assign value to a struct's field directly after type assertion? [duplicate]

This question already has an answer here:
Removing an element from a type asserted Slice of interfaces
(1 answer)
Closed 2 years ago.
Here is the struct BiOperators. It implements MyInterface1 (using BiOperators as method receiver). These are just some code for testing and I omit it here.
type BiOperators struct {
oprt1 float64
oprt2 float64
}
My question is why I can't assign value to oprt1 after type assertion
func testTypeAssertion() {
bio := BiOperators{111, 222}
arr := []MyInterface1{bio, &bio}
// I can do the conversion and output field oprt2
fmt.Println((arr[0].(BiOperators)).oprt2)
// But when I want to directly assign value to it, I got the following error
// cannot assign to arr[0].(BiOperators).oprt2go
// (arr[0].(BiOperators)).oprt2 = 99 <----this line is error
// and I can't create a pointer to do the assignment
// the following line has error : cannot take the address of arr[0].(BiOperators)go
// bioCvt2 := &(arr[0].(BiOperators)) <----this line is error too
// I can using the following lines to do the assignment. But it will create a new struct
bioCvt := (arr[0].(BiOperators))
bioCvt.oprt2 = 333
fmt.Println("bioCvt", bioCvt)
fmt.Println("arr[0](assigned using bio)", arr[0])
}
So is there anyway to NOT create a new struct after type assertion to do the field assignment?
When you put bio into the interface array, a copy of bio is made and stored in the zeroth element. The first element contains a pointer to bio so no copy is made for that element. Similarly, when you cast from the interface back to the struct type, another copy of the value is made. Presumably the compiler or language does not allow this because you'd be modifying a copy that you cannot access again to use the value in any way.
Even with your code above, something similar still happens. When the field on bioCvt is modified, it does not modify bio at all because you are modifying a copy.
However, if you cast the first element to a pointer instead, you can modify it, which is the underlying value in bio:
func testTypeAssertion() {
bio := BiOperators{111, 222}
arr := []MyInterface1{bio, &bio}
(arr[1].(*BiOperators)).oprt2 = 99 // does not error anymore
fmt.Println("arr[1](assigned using bio)", arr[1])
fmt.Println("bio", bio) // This will print 99
}

How do I get the type of struct in Go? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I am working on a demo project to understand GO language. I have defined one interface and 2 struct type. Also, I have defined an array of interfaces. Depending on the user input, I define each element within the array as a type of struct. During the data manipulation, I want to check the type of the struct defined on the array.
I have tried to use reflect, but unfortunately it did not worked. Also other methods on internet did not worked. I received messages such as panic or json cannot Unmarshal.
type Main_interface interface {
}
type Struct1 struct {
Index int
ID int
Name string
}
type Struct2 struct {
Index int
Timestamp string
Temporary_ID int
}
var MyArray []Main_interface
//...
NewStruct1Block := generateStruct1Block(...)
MyArray = append(MyArray, NewStruct1Block)
//...
NewStruct2Block := generateStruct2Block(...)
MyArray = append(MyArray, NewStruct2Block)
UPDATE: I want to be able to check the kind of struct implements the interface at runtime, depending on the user input.
Your array can be heteregenious, that is, it can contain elements of different types. You can't ensure (at compile time) that all elements are of the same type.
That said, you can check a single element using the type cast syntax.
You have three options:
Option 1: Check against a specific type
var unknown Main_Interface = getSomethingSomewhere()
result, ok := unknown.(Struct1);
if ok {
// result has type `Struct1`
}
Option 2: Use a switch statement to cover multiple types (already presented in another answer)
switch t := unknown.(type) {
case Struct1:
// t has type `Struct1` within this block (at compile time)
case Struct2:
// t has type `Struct2` within this block (at compile time)
default:
// neither Struct1 no Struct2
}
Option 3: compare against another type at runtime.
Note that types returned by reflect.TypeOf are comparable using the == operator, as documented here:
https://golang.org/pkg/reflect/#Type
Type values are comparable, such as with the == operator, so they can be used as map keys. Two Type values are equal if they represent identical types.
So we can do something like this:
var s1 Struct1 // empty struct
var s2 Struct2 // empty struct
if reflect.TypeOf(unknown) == reflect.TypeOf(s1) {
// unknown holds an s1 instance
}
But this is obviously not useful when you can just do the compile time check. Instead, you can compare two unknowns to check if they are the same:
var unknown1 Main_Interface = getSomethingSomewhere()
var unknown2 Main_Interface = getSomethingSomewhereElse()
if reflect.TypeOf(unknown1) == reflect.TypeOf(unknown2) {
// they are the same type
}
You need to use type assestion as below:
var MyArray []Main_interface
NewStruct1Block := Struct1{}
NewStruct2Block := Struct2{}
MyArray = append(MyArray, NewStruct1Block)
MyArray = append(MyArray, NewStruct2Block)
fmt.Printf("%v", MyArray)
switch t := MyArray[0].(type) {
case Struct1:
fmt.Printf("%v", t)
t.ID = 1
case Struct2:
fmt.Printf("%v", t)
t.Timestamp = "A"
default:
fmt.Print("unknown")
}
Working code here:
https://play.golang.org/p/OhpBDJu_q2x

cannot assign to struct field in map

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

Assign default value for struct field [duplicate]

This question already has answers here:
How to set default values in Go structs
(11 answers)
Closed 5 years ago.
I want to assign default value for struct field in Go. I am not sure if it is possible but while creating/initializing object of the struct, if I don't assign any value to the field, I want it to be assigned from default value. Any idea how to achieve it?
type abc struct {
prop1 int
prop2 int // default value: 0
}
obj := abc{prop1: 5}
// here I want obj.prop2 to be 0
This is not possible. The best you can do is use a constructor method:
type abc struct {
prop1 int
prop2 int // default value: 0
}
func New(prop1 int) abc {
return abc{
prop1: prop1,
prop2: someDefaultValue,
}
}
But also note that all values in Go automatically default to their zero value. The zero value for an int is already 0. So if the default value you want is literally 0, you already get that for free. You only need a constructor if you want some default value other than the zero value for a type.

Resources