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
}
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
This structure is very small, rendering a pointer useless.
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)
}
}
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
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With