Golang无法对指向切片的指针进行范围遍历。

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

Golang cannot range over pointer to slice

问题

当我尝试对一个切片指针进行范围遍历时,我一直遇到这个错误。

app/domain/repositories/class_repository.go:24: cannot range over classes (type *[]entities.Class)

我做错了什么?

这是结构体:

package repositories

import (
	"mobifit/app/domain/entities"
)

type ClassRepository struct {
	*Repository
}

func (c *ClassRepository) ClassesForLastNDays(days int) *[]entities.Class {
	classes := new([]entities.Class)
	query := Select("*").
		From("Class").
		Where("VisibleAt > CURRENT_TIMESTAMP() - INTERVAL ? DAY").
		OrderBy("ClassTypeId").
		Sql()
	c.Repository.Select(classes, query, days)
	c.populateClassRelationships(classes)
	return classes
}

func (c *ClassRepository) populateClassRelationships(classes *[]entities.Class) {
	for i := range classes {  <<<<<<< 这里是问题所在
		class := classes[i]

		// ClassType
		c.Repository.GetById(class.ClassType, class.ClassTypeId)

		//Instructor
		c.Repository.GetById(class.Instructor, class.ClassType.InstructorId)

		// Equipment
		query := Select("E.*").
			From("Equipment E").
			Join("ClassEquipment CE on E.Id = CE.EquipmentId").
			Where("CE.ClassId = ?").
			Sql()
		c.Repository.Select(class.Equipment, query, class.Id)
	}
}

这是Class结构体:

package entities

import (
	"time"
)

type Class struct {
	Id                int
	ClassTypeId       int
	VideoPath         string
	VideoSize         int
	Duration          float64
	CreatedAt         time.Time
	VisibleAt         time.Time
	NoLongerVisibleAt time.Time

	// Relationships
	ClassType  ClassType
	Instructor User
	Equipment  []Equipment
}

请帮我翻译以上内容。

英文:

I keep getting this error when trying to range over a slice pointer.

app/domain/repositories/class_repository.go:24: cannot range over classes (type *[]entities.Class)

What am I doing wrong?

Here is the struct:

 package repositories
    
import (
	&quot;mobifit/app/domain/entities&quot;
)

type ClassRepository struct {
	*Repository
}

func (c *ClassRepository) ClassesForLastNDays(days int) *[]entities.Class {
	classes := new([]entities.Class)
	query := Select(&quot;*&quot;).
		From(&quot;Class&quot;).
		Where(&quot;VisibleAt &gt; CURRENT_TIMESTAMP() - INTERVAL ? DAY&quot;).
		OrderBy(&quot;ClassTypeId&quot;).
		Sql()
	c.Repository.Select(classes, query, days)
	c.populateClassRelationships(classes)
	return classes
}

func (c *ClassRepository) populateClassRelationships(classes *[]entities.Class) {
	for i := range classes {  &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; Here is the problem
		class := classes[i]

		// ClassType
		c.Repository.GetById(class.ClassType, class.ClassTypeId)

		//Instructor
		c.Repository.GetById(class.Instructor, class.ClassType.InstructorId)

		// Equipment
		query := Select(&quot;E.*&quot;).
			From(&quot;Equipment E&quot;).
			Join(&quot;ClassEquipment CE on E.Id = CE.EquipmentId&quot;).
			Where(&quot;CE.ClassId = ?&quot;).
			Sql()
		c.Repository.Select(class.Equipment, query, class.Id)
	}
}

Here is the Class struct:

package entities

import (
	&quot;time&quot;
)

    type Class struct {
    	Id                int
    	ClassTypeId       int
    	VideoPath         string
    	VideoSize         int
    	Duration          float64
    	CreatedAt         time.Time
    	VisibleAt         time.Time
    	NoLongerVisibleAt time.Time
    
    	// Relationships
    	ClassType  ClassType
    	Instructor User
    	Equipment  []Equipment
    }

答案1

得分: 38

你假设指向切片的指针会自动解引用进行迭代。

事实并非如此,也没有理由这样做,因为切片本身已经是一种指针,使得指向切片的指针完全无用。

根据《Effective Go》的说明:

如果一个函数接受一个切片参数,并对切片的元素进行更改,这些更改将对调用者可见,类似于传递指向底层数组的指针。

在内部,切片由以下部分组成:

  • 指向底层数组中切片的第一个元素的指针
  • 切片的长度
  • 切片的容量(切片通常可以扩展到数组的末尾)

这个结构非常小,使得指针变得无用。

英文:

You're assuming the pointer to a slice will be automatically dereferenced for the iteration.

That's not the case and there's no reason for that because a slice is already a kind of pointer, rendering a pointer to a slice totally useless.

From Effective Go :

> If a function takes a slice argument, changes it makes to the elements
> of the slice will be visible to the caller, analogous to passing a
> pointer to the underlying array.

Internally, a slice is made of

  • a pointer to the first element of the slice in the underlying array
  • the length of the slice
  • the capacity of the slice (the slice can usually be extended until the end of the array)

This structure is very small, rendering a pointer useless.

答案2

得分: 14

如果你需要从*slice中提取单个元素,你首先需要解引用它,像这样:(*slice)[0]。在我意识到这一点之前,我花了大约6个小时纠结于*slice[0]。这与操作顺序有关,我认为这不是一个非常优雅的结果。

最后,我编写了一些指针接收器方法,以更合理的方式进行原地修改,比如追加和弹出元素 - 一个示例可以在这里找到:https://play.golang.org/p/qZEYMcPHl4

英文:

if you need to pull an individual element from the *slice, you have to dereference it first like this: (*slice)[0]. I pounded my head against *slice[0] for about 6 hours before I realized this. It has to do with the order of operations, and is not, IMO, a very elegant result.

I ended up writing some pointer receiver methods to do in-place modifications like append and pop in a more, to my mind, reasonable way - an example can be found here: https://play.golang.org/p/qZEYMcPHl4

答案3

得分: 12

Effective Go中:

如果你正在遍历数组、切片、字符串、映射或从通道中读取数据,可以使用range子句来管理循环。

你正在尝试迭代一个指向切片的指针,而这个指针是一个单值,不是一个集合,因此不可能实现。

populateClassRelationships的参数更改为切片,而不是指向切片的指针。或者你可以解引用指针:

func (c *ClassRepository) populateClassRelationships(classes []entities.Class) {
    for i := range classes { // 解引用指针以获取实际的切片
        class := classes[i]

        // ClassType
        c.Repository.GetById(class.ClassType, class.ClassTypeId)

        //Instructor
        c.Repository.GetById(class.Instructor, class.ClassType.InstructorId)

        // Equipment
        query := Select("E.*").
            From("Equipment E").
            Join("ClassEquipment CE on E.Id = CE.EquipmentId").
            Where("CE.ClassId = ?").
            Sql()
        c.Repository.Select(class.Equipment, query, class.Id)
    }
}
英文:

From Effective Go:
> If you're looping over an array, slice, string, or map, or reading
> from a channel, a range clause can manage the loop.

You are attempting to iterate over a pointer to a slice which is a single value, not a collection therefore is not possible.

Change the argument to populateClassRelationships to be an slice, not a pointer to a slice. Or you could dereference the pointer:

func (c *ClassRepository) populateClassRelationships(classes *[]entities.Class) {
	for i := range *classes { // dereferencing the pointer to get the actual slice
		class := classes[i]

		// ClassType
		c.Repository.GetById(class.ClassType, class.ClassTypeId)

		//Instructor
		c.Repository.GetById(class.Instructor, class.ClassType.InstructorId)

		// Equipment
		query := Select(&quot;E.*&quot;).
			From(&quot;Equipment E&quot;).
			Join(&quot;ClassEquipment CE on E.Id = CE.EquipmentId&quot;).
			Where(&quot;CE.ClassId = ?&quot;).
			Sql()
		c.Repository.Select(class.Equipment, query, class.Id)
	}
}

答案4

得分: 5

你可以解引用指针:

func (c *ClassRepository) populateClassRelationships(classes *[]entities.Class) {
    for _, class := range *classes { // 注意 * 解引用
        // ClassType
        c.Repository.GetById(class.ClassType, class.ClassTypeId)

        // Instructor
        c.Repository.GetById(class.Instructor, class.ClassType.InstructorId)

        // Equipment
        query := Select("E.*").
            From("Equipment E").
            Join("ClassEquipment CE on E.Id = CE.EquipmentId").
            Where("CE.ClassId = ?").
            Sql()
        c.Repository.Select(class.Equipment, query, class.Id)
    }
}

我还更改了 range 语句,因为我认为你没有修改 classes

英文:

You could dereference the pointer:

func (c *ClassRepository) populateClassRelationships(classes *[]entities.Class) {
    for _, class := range *classes { // NOTE the * dereference
	// ClassType
	c.Repository.GetById(class.ClassType, class.ClassTypeId)

	//Instructor
	c.Repository.GetById(class.Instructor, class.ClassType.InstructorId)

	// Equipment
	query := Select(&quot;E.*&quot;).
	    From(&quot;Equipment E&quot;).
	    Join(&quot;ClassEquipment CE on E.Id = CE.EquipmentId&quot;).
	    Where(&quot;CE.ClassId = ?&quot;).
	    Sql()
	c.Repository.Select(class.Equipment, query, class.Id)
    }
}

I also changed the range clause as I don't think you're modifying classes.

huangapple
  • 本文由 发表于 2014年1月22日 16:43:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/21278023.html
匿名

发表评论

匿名网友

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

确定