在这个上下文中,”make”函数对地图执行什么操作?

huangapple go评论80阅读模式
英文:

What does the "make" function do to a map in this context

问题

我刚刚了解Go语言,几天前我在寻找有关表单的教程,现在我对它有了一些了解,我试图创建自己的错误处理程序,可以在所有结构体中使用,有点像一个抽象类,但是我从教程中得到的示例让我有点困惑。

下面是我用来测试make函数的一个小例子。通过尝试,我有点明白它的作用,但我不明白它实际上是在做什么,以及为什么需要使用它。

type ErrorHandler struct {
    Errors map[string]string
}

type Form struct {
    ErrorHandler
}

func main() {
    form := &Form{}

    if true {
        fmt.Printf("%p\n", &form.Errors)
    } else {
        form.Errors = make(map[string]string)
        fmt.Printf("%p\n", &form.Errors)
    }
}

在上面的示例中,我尝试将if语句从true改为false,以查看基于是否使用make函数,内存地址是否会发生变化,但两种情况下地址都保持不变。我在这里阅读了答案,他说它的一个用途是“创建一个预分配空间的映射”——对我来说这并不意味着太多,因为我对指针和相关知识还不熟悉,但是“创建一个映射”的部分让我想到它是在重新初始化ErrorHandler结构中的Errors映射,这会将其分配给一个新的内存地址,对吗?但实际上它们保持不变。

所以我尝试在映射中创建一个值,一次是不使用make函数,一次是使用make函数。if条件给我一个错误,说“在nil映射中的条目赋值”和“goroutine [running]”,而else语句打印出“haha”,这是我设置的值:

type ErrorHandler struct {
    Errors map[string]string
}

type Form struct {
    ErrorHandler
}

func main() {
    form := &Form{}

    if true {
        form.Errors["blah"] = "haha"
        fmt.Printf(form.Errors["blah"])
    } else {
        form.Errors = make(map[string]string)
        form.Errors["blah"] = "haha"
        fmt.Printf(form.Errors["blah"])
    }
}

所以我有点明白make函数在做什么,但不完全清楚。据我所知,如果我要向该映射中“添加”项,我需要使用make函数,但我不明白为什么需要这样。为什么我的结构体中的map[string]string被设置为nil,因为当我打印它时,我看到的是“map[]”,而不是“nil”...这让人困惑。

有人能解释一下吗?这里的goroutine是如何使用的?也许这就是我要找的答案...我以前从未使用过它。

另外,既然我必须使用make,有没有办法自动执行它,而不是在每个方法的顶部都要写一遍?

例如,在我的ErrorHandler结构体中,我有一个方法,如下所示:

func (this *ErrorHandler) HandleErr(err string) {
    this.Errors = make(map[string]string)
    this.Errors["Error"] = err
}

在我的Form结构体中,我还有另一个验证表单的方法,但也在方法的顶部使用了this.Errors = make(map[string]string)...感觉不够DRY(Don't Repeat Yourself)。

非常感谢任何帮助。

英文:

I am new to Go and was looking for a tutorial on forms a few days ago, and now that I've familiarized myself a bit more I was attempting to create my own error handler that I could use with all of my structs, sort of like an abstract class, but the example I got from the tutorial I saw has me a bit stumped.

Here is a small example I played with to test what the make function was doing. I kind of figured it out by tinkering, but I don't understand what it's actually doing, and why it is necessary.

type ErrorHandler struct {
    Errors map[string]string
}

type Form struct {
    ErrorHandler
}

func main() {
    form := &Form{}

    if true {
        fmt.Printf("%p\n", &form.Errors)
    } else {
        form.Errors = make(map[string]string)
        fmt.Printf("%p\n", &form.Errors)
    }
}

In the example above, I try changing the if statement from true to false to see if the memory address changes based on whether the make function is used or not and it stays the same both cases. I read the answer here and he said one of its uses is to "create a map with space preallocated" -- which honestly doesn't mean much to me since I'm new to pointers and all that, but the "create a map" part had me thinking it's like reinitializing that Errors map in the ErrorHandler struct, which would assign it to a new memory address, yes? But no, they stay the same.

So I tried creating a value in the map, once without the make function and once with the make function. The if condition gives me an error saying assignment to entry in nil map and goroutine [running], and the else statement prints "haha", which is what I set it to:

type ErrorHandler struct {
    Errors map[string]string
}

type Form struct {
    ErrorHandler
}

func main() {
    form := &Form{}

    if true {
        form.Errors["blah"] = "haha"
        fmt.Printf(form.Errors["blah"])
    } else {
        form.Errors = make(map[string]string)
        form.Errors["blah"] = "haha"
        fmt.Printf(form.Errors["blah"])
    }
}

So I sort of see what the make function is doing, but not exactly. As far as I can tell if I am going to "push" items into that map I need to use the make function, but I don't see why it's necessary. Why is the map[string]string set to nil in my struct because when I print it out I see "map[]" and not "nil"... that's confusing.

Can someone explain please? And how is this goroutine used here? Maybe that's the answer I'm looking for.. never used that before.

Also, since I have to use make, is there a way to kind of make it happen automatically instead of having to put it at the top of each method?

For example in my ErrorHandler struct I have a method that looks like this:

func (this *ErrorHandler) HandleErr(err string) {
    this.Errors = make(map[string]string)
    this.Errors["Error"] = err
}

and in my Form struct I also have another method that is validating the form, but also uses the `this.Errors = make(map[string]string) at the top of the method... feels non-DRY to me.

Any help is greatly appreciated.

答案1

得分: 1

将地图视为一个包含指向底层地图结构的指针和大小的结构体(可能还有其他内容,不确定)。

例如,可以这样考虑:

struct Map {
  data *Entries
  size int
}

make函数根据传递给make的大小分配Entries的初始值(如果有的话)。

当你说:

form.Errors = make(map[string]string)

就相当于说:

form.Errors = struct { data : malloc(sizeof(Entries)*capacity), size : 0 }

这将把"data"和"size"变量复制到form.Errors中。

实际字段"form.Errors"在内存中仍然是同一个位置,form.Errors的内部值已经改变以匹配make返回的值。

因此,当你查看&form.Errors时,地址不会改变。

至于在向未使用make函数创建的地图中添加元素时出现的nil错误...

未初始化的地图尚未具有"data"段,因此会出现空指针错误。

就像执行以下操作一样:

var i *int
*i = 5

也会导致空指针错误。

希望以上解释有助于消除困惑。

英文:

Think of a map as a struct containing a pointer to the underling map structure and a size (there may be other stuff, not sure).

For example, think of it like:

struct Map {
  data *Entries
  size int
}

make, allocates the initial value of Entries according to the size passed to make (if any)

When you say:

form.Errors = make(map[string]string)

it's like saying:

form.Errors = struct { data : malloc(sizeof(Entries)*capacity), size : 0 }

which copies the "data" and "size" variables in to your form.Errors

The actual field "form.Errors" is still the same place in memory, the internal values of form.Errors have changed to match what make returned.

So, the address doesn't change when you look at &form.Errors

As for the nil error when pushing to a map you didn't make...

An un-maid map has no "data" segment yet, so you get a nil pointer error.

Not unlike doing:

var i *int
*i = 5

Also results in a nil error

Hopefully that all made sense and helps shed a bit of light on the confusion.

答案2

得分: 1

首先,回答你的问题:

从https://golang.org/pkg/builtin/#make

> make内置函数用于分配和初始化切片、映射或通道类型的对象[...]
>
> 映射:根据指定的大小进行初始分配,但生成的映射长度为0。大小可以省略,此时会分配一个较小的初始大小。

其次,你提供的代码中没有使用goroutine。

第三,make函数初始化映射,你可能不应该在每次HandleErr调用中都使用它,因为这会替换掉你当前的映射。我认为你可能需要一个类似于NewErrorHandlerNewForm的初始化函数,它可以为你的结构体创建实例。例如:

func NewErrorHandler() ErrorHandler {
    return ErrorHandler{make(map[string]string)}
}

最后,为什么映射不能自动初始化呢?如果自动初始化,你将无法控制映射的初始大小。如果你的代码非常重要,你希望它运行快速或占用较少的内存,并且知道映射的大小,这可能很重要。

另外,还有一种初始化映射的方法。如果你想要初始化一个空映射,可以写成:map[string]string{}。如果你想要创建一个带有初始值的映射:

map[string]string{
    "a": "b",
    "c": "d",
}

因此,你可以将NewErrorHandler函数写成:

func NewErrorHandler() ErrorHandler {
    return ErrorHandler{map[string]string{}}
}
英文:

First, to answer your question:

from https://golang.org/pkg/builtin/#make

> The make built-in function allocates and initializes an object of type slice, map, or chan (only) [...]
>
> Map: An initial allocation is made according to the size but the
resulting map has length 0. The size may be omitted, in which case
a small starting size is allocated.

Secondly, there aren't goroutines in the code you posted.

Thirdly, make initializes the map and you probably shouldn't use it in every HandleErr call, because it would replace your current map. I think that what you want may be an initialization function like NewErrorHandler or NewForm. It is kind of make for your struct. For example:

func NewErrorHandler() ErrorHandler {
    return ErrorHandler{make(map[string]string)}
}

And finally why can't map initialize automatically? If it did you wouldn't have any control over initial size of your map. It may matter if you have mission critical code and you want it to be fast or low memory and know the size.

PS. There is also alternative way to initialize a map. If you want initialize an empty map you can write: map[string]string{}. If you want to create a map with initial values:

map[string]string{
    "a": "b",
    "c": "d",
}

So you could write you NewErrorHandler function like:

func NewErrorHandler() ErrorHandler {
    return ErrorHandler{map[string]string{}}
}

huangapple
  • 本文由 发表于 2016年1月23日 12:04:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/34959485.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定