Golang在遍历结构体切片时的内存地址问题

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

Golang memory address issue when range over slice of structs

问题

我对Go语言非常陌生,所以对于这个问题可能有一个非常简单的解决方法。我在调用数组中结构体内部接口的方法时遇到了问题。我认为通过一个例子来解释会更好,所以这是我能够简化代码的最基本的例子。

package main

import "fmt"

/* 测试类型 */
type Test interface {
	Hello() string
}

type TestRecord struct {
	test Test
	name string
}

type TestList []TestRecord

/* 其他类型 */
type Other struct{}

func (o Other) Hello() string {
	return "Hello From Other"
}

func main() {
	/* 将TestList初始化为t */
	t := make(TestList, 1, 100)

	/* 创建TestRecord并将其添加到列表中 */
	tr := TestRecord{Other{}, "OtherRecord"}
	t = append(t, tr)

	/* 循环遍历列表 */
	for _, otr := range t {
		fmt.Println(otr.name)
		fmt.Println(otr.test.Hello())
	}
}

当我尝试执行这段代码时,我得到以下错误:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x400efd]

在这个设置中,我应该如何正确地在循环遍历数组时调用Hello方法呢?

英文:

I am very new to Go so there is probably a very simple fix for this. I am having trouble calling a method of an interface that is within a struct in an array. I think this is best explained through an example, so here is the most basic example I was able to boil my code down to.

package main

import "fmt"

/* Test Types */
type Test interface {
	Hello() string
}

type TestRecord struct {
	test Test
	name string
}

type TestList []TestRecord

/* Other Type */
type Other struct{}

func (o Other) Hello() string {
return "Hello From Other"
}

func main() {
	/* Init TestList into t */
	t := make(TestList, 1, 100)

	/* Create TestRecord and add it to the list */
	tr := TestRecord{Other{}, "OtherRecord"}
	t = append(t, tr)

	/* Loop over list and  */
	for _, otr := range t {
		fmt.Println(otr.name)
		fmt.Println(otr.test.Hello())
	}
} 

When I try to execute this, I get the following error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x400efd]

How do I properly call Hello while looping through the array in this setup?

答案1

得分: 7

你的问题出在这一行:

 t := make(TestList, 1, 100)

在这里,你已经将t初始化为一个已经有一个TestRecord的列表,但是你没有设置该TestRecord中的任何字段,所以t[0].testnil。你不能在nil接口上调用方法,因为Go不知道要调用哪个方法。

最简单的修复方法(对源代码的更改最小)是使用以下语句以一个空的TestList开始:

t := make(TestList, 0, 100)

然而,如果你愿意,你也可以依靠append根据需要分配t,并将t声明为nilTestList,如下所示:

var t TestList

(我建议在你的测试程序中使用第二个版本,因为你并没有使用到全部的容量,但我认识到你可能将其从一个实际程序中简化而来,在那种情况下提前指定容量实际上是有帮助的。)


在nil值上调用方法

  • 你可以在nil结构体指针上调用方法,因为Go可以在编译时确定应该调用哪个函数。然而,如果该函数对结构体指针进行解引用,那么你将在解引用的位置遇到相同的崩溃。
  • 当你通过接口访问该指针时,Go需要对其进行解引用以确定应该调用哪个函数,所以程序在进行函数调用时立即崩溃。
英文:

Your problem is in the line

 t := make(TestList, 1, 100)

Here, you've initialized t to already have one TestRecord in it, but you didn't set any of the fields in that TestRecord, so t[0].test is nil. You can't call methods on a nil interface, because Go doesn't know what method to call.

The simplest fix (the one that changes your source code the least) is to start with an empty TestList, using the following statement:

t := make(TestList, 0, 100)

However, if you wish, you can also rely on append to allocate t as necessary, and start with a nil TestList by declaring t as follows:

var t TestList

(I recommend the second version in your test program, since you're not using the full capacity anyway, however I recognize that you may have condensed this from a real program where specifying the capacity in advance is actually helpful.)


Calling methods on a nil value

  • You can call methods on a nil struct pointer, because Go can determine what function should be called at compile time. However, if that function dereferences the struct pointer, then you'll get this same crash at the site of the dereference.
  • When you're accessing this pointer through an interface, Go needs to dereference it to figure out which function to call, so the program crashes as soon as you make the function call.

huangapple
  • 本文由 发表于 2014年6月15日 20:39:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/24229658.html
匿名

发表评论

匿名网友

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

确定