复制带有嵌套地图的结构[重复]

This question already has an answer here:

What am I trying to do?

Copy a "default" struct into a new one when needed, keeping all it's values.

Details

I am trying to copy a Chat struct:

type ChatData struct {
        User map[string]map[string]string `json:"user"`
        Chat map[string]string            `json:"chat"`
}
type Chat struct {
        Settings map[string]map[string]interface{} `json:"settings"`
        Data     ChatData                          `json:"data"`
}

I only need to do this when a new chat is introduced, and I check for membership in a map[string]Chat.

//data is a map[string]Chat
if _, exists := data[event.Chat]; !exists {
        data[event.Chat] = data["default"]
}

The full default struct is:

{
    "default": {
        "settings": {
            "eightball": {
                "messages": [
                    "yes",
                    "no",
                    "maybe"
                ]   
            },  
            "main": {
                "blacklistedCommands": [], 
                "blacklistedUsers": [], 
                "error": "there was an error - ",
                "maxConsecutive": 5,
                "permissions": "You don't have permissions for that command.",
                "success": "The command was successful.",
                "whitelistedCommands": [], 
                "whitelistedUsers": []
            }   
        },  
        "data": {
            "user": {
                "default": {
                    "admin": "false",
                    "consecutiveCommands": "0",
                    "nickname": "", 
                    "sentMessages": "0" 
                },  
                "total": {
                    "admin": "false",
                    "consecutiveCommands": "0",
                    "nickname": "", 
                    "sentMessages": "0" 
                }   
            },  
            "chat": {
                "commandSender": "", 
                "lastMessage": "", 
                "lastSender": "", 
                "lastTimestamp": "", 
                "wasCommand":""
            }
        }
    }
}

What have I tried

data[event.Chat] = data["default"]
// causes a reference

data[event.Chat] = &data["default"]
// cannot use &data["default"] (type *Chat) as type Chat in assignment

data[event.Chat] = *data["default"]
// invalid indirect of data["default"] (type Chat)

Do I need to change my use of map[string]Chat to map[string]*Chat and go with the second option? Pointers and references are not my specialty, so help would be appreciated.

Edit

whoever thought I was copying a map, what are you smoking?

</div>

I have found in previous cases, that an easy (though not the most efficient) way to copy a complex structure is to Marshal it, and then Unmarshal it into a new variable. This can be done with a variety of encodings. Two easy ones (included in the stdlib) would be json and gob.

There are also plenty of libraries using reflection to achieve a similar goal: https://github.com/jinzhu/copier

But like I said, though not as efficient, this is easy to reason about and takes just a single, simple function to achieve. If performance matters you should prefer gob over json. If performance REALLY matters, then you should prefer another solution altogether.

If you know the data structure it is best for you to fully define your structs. It will be much easier to manage than complex maps and interfaces with reflection to cast data types. Here is a tool to help you convert JSON to Go structs. It isn't perfect and you have to confirm the data types. You may even want to rip out the internal structs and create types for them for readability and maintenance, but it will save you some time.

https://mholt.github.io/json-to-go/

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

var rawDataExample = []byte(`{
    "default": {
        "settings": {
            "eightball": {
                "messages": [
                    "yes",
                    "no",
                    "maybe"
                ]   
            },  
            "main": {
                "blacklistedCommands": [], 
                "blacklistedUsers": [], 
                "error": "there was an error - ",
                "maxConsecutive": 5,
                "permissions": "You don't have permissions for that command.",
                "success": "The command was successful.",
                "whitelistedCommands": [], 
                "whitelistedUsers": []
            }   
        },  
        "data": {
            "user": {
                "default": {
                    "admin": "false",
                    "consecutiveCommands": "0",
                    "nickname": "", 
                    "sentMessages": "0" 
                },  
                "total": {
                    "admin": "false",
                    "consecutiveCommands": "0",
                    "nickname": "", 
                    "sentMessages": "0" 
                }   
            },  
            "chat": {
                "commandSender": "", 
                "lastMessage": "", 
                "lastSender": "", 
                "lastTimestamp": "", 
                "wasCommand":""
            }
        }
    }
}
`)

type Settings struct {
    Default struct {
        Settings struct {
            Eightball struct {
                Messages []string `json:"messages"`
            } `json:"eightball"`
            Main struct {
                BlacklistedCommands []string `json:"blacklistedCommands"`
                BlacklistedUsers    []string `json:"blacklistedUsers"`
                Error               string   `json:"error"`
                MaxConsecutive      int      `json:"maxConsecutive"`
                Permissions         string   `json:"permissions"`
                Success             string   `json:"success"`
                WhitelistedCommands []string `json:"whitelistedCommands"`
                WhitelistedUsers    []string `json:"whitelistedUsers"`
            } `json:"main"`
        } `json:"settings"`
        Data struct {
            User struct {
                Default struct {
                    Admin               string `json:"admin"`
                    ConsecutiveCommands string `json:"consecutiveCommands"`
                    Nickname            string `json:"nickname"`
                    SentMessages        string `json:"sentMessages"`
                } `json:"default"`
                Total struct {
                    Admin               string `json:"admin"`
                    ConsecutiveCommands string `json:"consecutiveCommands"`
                    Nickname            string `json:"nickname"`
                    SentMessages        string `json:"sentMessages"`
                } `json:"total"`
            } `json:"user"`
            Chat struct {
                CommandSender string `json:"commandSender"`
                LastMessage   string `json:"lastMessage"`
                LastSender    string `json:"lastSender"`
                LastTimestamp string `json:"lastTimestamp"`
                WasCommand    string `json:"wasCommand"`
            } `json:"chat"`
        } `json:"data"`
    } `json:"default"`
}

type Data struct {
    Events map[string]Settings
}

func main() {
    var settings Settings
    err := json.Unmarshal(rawDataExample, &settings)
    if err != nil {
        log.Fatal(err)
    }

    event := "foo"
    d := Data{
        Events: make(map[string]Settings),
    }

    if _, ok := d.Events[event]; !ok {
        // event doesn't exist
        // add it
        d.Events[event] = settings
        fmt.Println("event added")
    }

    if _, ok := d.Events[event]; !ok {
        // event exist, this will never be happen
        // sanity check
        fmt.Println("this will never be printed")
    }

    fmt.Printf("%+v
", d)

}

Super simple fix, just wanted to get some input before I did it. Changed the map[string]Chat > map[string]*Chat and made a new *Chat, then copied the data.

data[event.Chat] = &Chat{}
*data[event.Chat] = *data["default"]