-1

I'm struggling with updating a slice inside map rulesByCountry without any success. The value of enabled changes only inside the function UpdateName but the map itself still sees this value unchanged. I assume it's something to do with pointers. I guess I did not grasp the concept of it. Can someone direct me what I'm doing wrong here? I tried a lot of things and run out of options. I would appreciate any kind of help.


import (
    "fmt"
)
// Consts
const RuleSeparateStreetNameFromHome string = "Separate street number from home"

// Types
type Address struct {
    AddressLines []string
    Country      string
}

type RuleChain []RuleDefinition

type RuleDefinition struct {
    Name string
    Enabled bool
}

//Map
var rulesByCountry map[string][]RuleDefinition = map[string][]RuleDefinition{
    "DE": {
        {
            Name: RuleSeparateStreetNameFromHome,
            // TODO some logic,
            Enabled: false,
        },
    },
}

func main() {
    var addr *Address
    addr = &Address{
        AddressLines: []string{
            "Street3",
        },
        Country: "DE",
    }
    rules := GetRulesForCountry(addr.GetCountry())
    rules.UpdateName(RuleSeparateStreetNameFromHome)
    fmt.Println(rules)
}

func GetRulesForCountry(country string) RuleChain {
    if rules, ok := rulesByCountry[country]; ok {
        return rules
    }
    return nil
}

func (a *Address) GetFirstAddressLine() string {
    return a.GetAddressLine(1)
}

func (a *Address) GetAddressLine(lineNumber int) string {
    if lineNumber <= 0 {
        return ""
    }
    lines := a.GetAddressLines()
    if len(lines) >= lineNumber {
        return lines[lineNumber-1]
    }
    return ""
}

func (m *Address) GetAddressLines() []string {
    if m != nil {
        return m.AddressLines
    }
    return nil
}

func (r *RuleChain) UpdateName(name string) {
    for _, rule := range *r {
        if rule.Name == name {
            rule.Enabled = true
            fmt.Print(rule)
        }
    }
}

func (m *Address) GetCountry() string {
    if m != nil {
        return m.Country
    }
    return ""
}
 
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Maciej Czarnota
  • 559
  • 2
  • 8
  • 16
  • 3
    The slice in the map is storing non-pointers, so when you are ranging over `*r`, each `rule` variable contains a copy of the value in the slice, and modifying that copy will have no effect on the value in the slice, of course. Use pointers in the slice. Or do `for i, rule := range *r` and then `(*r)[i].Enabled = true` to modify the value *in* the slice instead of the copy. – mkopriva Aug 22 '21 at 14:11
  • 1
    https://play.golang.org/p/HeCIIFqn2_n or https://play.golang.org/p/lPDaC2Se_vL – mkopriva Aug 22 '21 at 14:15
  • Thanks that works! I guess I have a long way to go to grasp this concept. Thank you for explaining – Maciej Czarnota Aug 22 '21 at 14:57
  • Does this answer your question? [Using Pointers in a for loop](https://stackoverflow.com/questions/48826460/using-pointers-in-a-for-loop) – blackgreen Aug 22 '21 at 16:09

1 Answers1

0

Based on the inputs of mkopriva

package main

import (
    "fmt"
)

// Consts
const RuleSeparateStreetNameFromHome string = "Separate street number from home"

// Types
type Address struct {
    AddressLines []string
    Country      string
}

type RuleChain []*RuleDefinition

type RuleDefinition struct {
    Name    string
    Enabled bool
}

//Map
var rulesByCountry map[string][]*RuleDefinition = map[string][]*RuleDefinition{
    "DE": {
        {
            Name: RuleSeparateStreetNameFromHome,
            // TODO some logic,
            Enabled: false,
        },
    },
}

func main() {
    var addr *Address
    addr = &Address{
        AddressLines: []string{
            "Street3",
        },
        Country: "DE",
    }
    rules := GetRulesForCountry(addr.GetCountry())
    rules.UpdateName(RuleSeparateStreetNameFromHome)

    fmt.Println(rules[0])
}

func GetRulesForCountry(country string) RuleChain {
    if rules, ok := rulesByCountry[country]; ok {
        return rules
    }
    return nil
}

func (a *Address) GetFirstAddressLine() string {
    return a.GetAddressLine(1)
}

func (a *Address) GetAddressLine(lineNumber int) string {
    if lineNumber <= 0 {
        return ""
    }
    lines := a.GetAddressLines()
    if len(lines) >= lineNumber {
        return lines[lineNumber-1]
    }
    return ""
}

func (m *Address) GetAddressLines() []string {
    if m != nil {
        return m.AddressLines
    }
    return nil
}

func (r *RuleChain) UpdateName(name string) {
    for _, rule := range *r {
        if rule.Name == name {
            rule.Enabled = true
            fmt.Print(rule)
        }
    }
}

func (m *Address) GetCountry() string {
    if m != nil {
        return m.Country
    }
    return ""
}

Output:

&{Separate street number from home true}&{Separate street number from home true}
Gopher
  • 721
  • 3
  • 8
  • you donot really need a ptr in the receiver. https://play.golang.org/p/f6NdHdCFb6o (the receiver is an alias to a slice, remember a slice has a ptr to the backng array, so if you make a copy of the header, it still points to the same backing array!) –  Aug 26 '21 at 19:31
  • I meant in `func (r RuleChain) UpdateName(name string) { for _, rule := range r {` –  Aug 26 '21 at 19:32