Golang中Map已赋值却显示为nil的问题如何解决

Golang中Map已赋值却显示为nil的问题如何解决 Go 版本:1.12.2 错误信息:panic: assignment to entry in nil map

我知道仅声明 var myMap map[string]int 会导致向 nil 映射赋值的问题,必须使用 myMap := make(map[string]int)。但我似乎在一个已经赋值过的映射上遇到了同样的问题。以下是简化后的代码。请注意,我为了排查问题添加了许多多余的内容(尽管我已经尽量删除了很多):

	for frm := range input {

		go func() {
			detMap := make(map[string][]image.Rectangle)
			tmpRect := []image.Rectangle{image.Rectangle{image.Point{0, 0}, image.Point{1, 1}}}
			detMap["init"] = tmpRect
			tmpImg, _, _, _, detMap, sublError = imageFrames.Sublimate(frm)

			rects := det.DetectMultiScale(tmpImg)
			detMap[fname] = rects

			frm.Detections = detMap
			output <- frm
			return
		}()
	}

我添加了 “tmpRect” 是为了确保 detMap 实际上不是 nil。ImageFrames.Sublimate() 如下:

func Sublimate(inFrm Frame) (gocv.Mat, string, []byte, map[int]image.Rectangle, map[string][]image.Rectangle, error) {

	retMap := make(map[string][]image.Rectangle)
	retMot := make(map[int]image.Rectangle)

	retMap = inFrm.Detections
	retMot = inFrm.MotionMap

	retMat, err := gocv.NewMatFromBytes(inFrm.Wid, inFrm.Hei, inFrm.Type, inFrm.MatBytes)

	return retMat, inFrm.UserName, inFrm.UserHash, retMot, retMap, err

}

即使我不预先创建映射,直接使用 tmpImg, _, _, _, detMap, sublError := imageFrames.Sublimate(frm),也会遇到同样的问题。


更多关于Golang中Map已赋值却显示为nil的问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

哪一行?

detMap[fname] = rects

更多关于Golang中Map已赋值却显示为nil的问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这源于我为了弄清楚发生了什么而对源代码进行的反复折腾。我不断添加和删除内容,试图找出问题发生的位置。从未想过问题根本不在这个过程中。

这里的panic来自哪一行?这应该能指出是哪一行的map访问操作中map为nil。

func main() {
    fmt.Println("hello world")
}

retMap = inFrm.Detections

这个值是 nil 吗?你在 Sublimate 函数中创建了 retMap,但随后用 inFrm.Detections 中的任何值覆盖了它,然后将其返回。

编辑: 哎呀。我漏看了你已经发现了。没关系!

问题已解决!

问题源于创建“Frame”类型。我创建它们的过程与从网络摄像头获取图像的过程完全不同。这些帧随后被编码并通过管道传输。当我最初创建Frame类型时,我编写了一个函数,用于在整个项目中需要创建新帧时生成新帧,但当时我并未在其中包含OpenCV检测结果的映射。

当我在项目中添加计算机视觉功能时,我没有更新创建空白帧的函数,而仅仅在结构体中添加了相应的字段。这意味着,此后创建的所有新帧在检测映射字段的位置上都会是一个nil值。

这个带有nil检测映射的帧会被编码并通过管道发送,从而在此过程中引发问题。感谢大家的帮助和建议。

尝试将 frm 的当前值传递给 goroutine。示例:

package main

import (
	"fmt"
	"time"
)

func main() {
	input := []int{2, 4, 6, 8, 10}

	for i := range input {
		go func(x int) {
			processValue(x)
		}(input[i])
	}
	time.Sleep(10)
}

func processValue(value int) {
	fmt.Println(value)
}

链接

或者用变量 “i” 替换 “input[i]”。否则,你可能会遇到并发问题。

package main

import (
	"fmt"
	"time"
)

func main() {
	input := []int{2, 4, 6, 8, 10}

	for i := range input {
		go func(x int) {
			processValue(x)
		}(i)
	}
	time.Sleep(10)
}

func processValue(value int) {
	fmt.Println(value)
}

链接 2

我尝试了那个方法,但最终还是遇到了同样的错误。我已经重写了函数,包含了来自 Sublimate() 的代码行,但我仍然遇到这个问题。我也完全移除了匿名函数,所以现在这只是一个带有输入通道和输出通道的串行循环。新的、未精简的版本如下:

func detect(input chan okoframe.Frame, output chan okoframe.Frame, classifierLocation string) {

	for frm := range input {

		//go func(frm okoframe.Frame) {
		var tmpImg gocv.Mat
		var sublError error
		//tmpImg, _, _, _, detMap, sublError := okoframe.Sublimate(frm)
		//StartSublimate--------------------------------------------------------------------------------------------
		tmpImg, nmfbError := gocv.NewMatFromBytes(frm.Wid, frm.Hei, frm.Type, frm.MatBytes)
		if nmfbError != nil {
			fmt.Println("Sublimate fail:", nmfbError)
		}

		//END SUBLIMATE---------------------------------------------------------------------------------------

		fname := filepath.Base(classifierLocation)

		fmt.Println("Loading classifier from :", fname)

		det := gocv.NewCascadeClassifier()
		if !det.Load(fname) {
			fmt.Errorf("Couldn't load classifier.")
			os.Exit(1)
		}

		rects := det.DetectMultiScale(tmpImg)
		//detMap[fname] = rects
		frm.Detections[fname] = rects
		//frm.Detections = detMap

		if sublError != nil {
			fmt.Println("Big error with sublimate stuff:", sublError)
			frm.ErrList = append(frm.ErrList, sublError)
		}

		output <- frm
		//return
		//}(frm)
	}
}

这真是太奇怪了。我确信这完全是我忽略的某个愚蠢的小问题,但是,唉。

问题出在 Sublimate 函数返回的 retMapinFrm.Detections 的副本。当 inFrm.Detectionsnil 时,retMap 也会是 nil。即使你在调用前初始化了 detMap,但函数返回时重新赋值了 detMap 变量,覆盖了之前初始化的映射。

以下是修复后的代码:

1. 修改 Sublimate 函数,避免返回 nil 映射:

func Sublimate(inFrm Frame) (gocv.Mat, string, []byte, map[int]image.Rectangle, map[string][]image.Rectangle, error) {
    // 确保返回的映射不为 nil
    retMap := inFrm.Detections
    if retMap == nil {
        retMap = make(map[string][]image.Rectangle)
    }
    
    retMot := inFrm.MotionMap
    if retMot == nil {
        retMot = make(map[int]image.Rectangle)
    }
    
    retMat, err := gocv.NewMatFromBytes(inFrm.Wid, inFrm.Hei, inFrm.Type, inFrm.MatBytes)
    return retMat, inFrm.UserName, inFrm.UserHash, retMot, retMap, err
}

2. 或者,在主代码中检查返回的映射:

for frm := range input {
    go func(frm Frame) {
        tmpImg, _, _, _, detMap, sublError := imageFrames.Sublimate(frm)
        if sublError != nil {
            // 处理错误
            return
        }
        
        // 检查 detMap 是否为 nil
        if detMap == nil {
            detMap = make(map[string][]image.Rectangle)
        }
        
        rects := det.DetectMultiScale(tmpImg)
        detMap[fname] = rects
        
        frm.Detections = detMap
        output <- frm
    }(frm)
}

3. 另一种方案是修改 Frame 结构体,确保 Detections 字段始终被初始化:

type Frame struct {
    // 其他字段...
    Detections map[string][]image.Rectangle
    MotionMap  map[int]image.Rectangle
}

// 在创建 Frame 时初始化映射
func NewFrame() Frame {
    return Frame{
        Detections: make(map[string][]image.Rectangle),
        MotionMap:  make(map[int]image.Rectangle),
    }
}

第一种方案是最直接的修复,确保 Sublimate 函数永远不会返回 nil 映射。这样调用方就不需要额外检查。

回到顶部