英文:
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 (
"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 { <<<<<<<<<<< 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("E.*").
From("Equipment E").
Join("ClassEquipment CE on E.Id = CE.EquipmentId").
Where("CE.ClassId = ?").
Sql()
c.Repository.Select(class.Equipment, query, class.Id)
}
}
Here is the Class struct:
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
}
答案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("E.*").
From("Equipment E").
Join("ClassEquipment CE on E.Id = CE.EquipmentId").
Where("CE.ClassId = ?").
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("E.*").
From("Equipment E").
Join("ClassEquipment CE on E.Id = CE.EquipmentId").
Where("CE.ClassId = ?").
Sql()
c.Repository.Select(class.Equipment, query, class.Id)
}
}
I also changed the range clause as I don't think you're modifying classes
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论