Голан не может располагаться по указателю на срез

Я продолжаю получать эту ошибку при попытке переместиться по указателю на фрагмент.

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 {  <<<<<<<<<<< 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)
    }
}

Вот структура класса:

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

Вы предполагаете, что указатель на срез будет автоматически разыменован для итерации.

Это не так, и нет причин для этого, потому что срез уже является своего рода указателем, что делает указатель на фрагмент абсолютно бесполезным.

Из Эффективный путь:

Если функция принимает аргумент среза, изменяется его значение на элементы фрагмента будет видна вызывающей стороне, аналогичной указатель на базовый массив.

Внутренне, срез сделан из

  • указатель на первый элемент среза в базовом массиве
  • длина среза
  • емкость среза (срез обычно может быть расширен до конца массива)

Эта структура очень мала, делая указатель бесполезным.

Ответ 2

От Эффективный переход:

Если вы перебираете массив, срез, строку или карту или читаете из канала, предложение диапазона может управлять циклом.

Вы пытаетесь выполнить итерацию по указателю на срез, который является единственным значением, а не коллекция, поэтому не представляется возможным.

Измените аргумент на populateClassRelationships как срез, а не на указатель на фрагмент. Или вы можете разыменовать указатель:

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)
    }
}

Ответ 3

если вам нужно вытащить отдельный элемент из среза *, вы должны сначала разыменовать его следующим образом: (*slice)[0]. Я стукнул головой на *slice[0] около 6 часов, прежде чем понял это. Это связано с порядком операций, и это не очень ИМО, очень элегантный результат.

В конце концов я написал несколько методов-приемников указателей, чтобы сделать модификации на месте, такие как append и pop в более, на мой взгляд, разумным способом - пример можно найти здесь: https://play.golang.org/p/qZEYMcPHl4

Ответ 4

Вы можете разыменовать указатель:

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)
    }
}

Я также изменил предложение диапазона, поскольку я не думаю, что вы изменяете classes.