尝试在切片指针上移动时,我一直收到此错误。

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

我究竟做错了什么?

这是结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 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)
    }
}

这是Class结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
    }
  • 切片已经是一种指针,没有理由指向它。
  • 我希望有一块指针,所以可以用PoulateClassRelationships函数填充它们
  • @dystroy我认为您现在实际上是最好的答案,因为您实际上已经找到了问题的根源。 根据golang.org/doc/effective_go.html#slices,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
  • 我曾经踩过同样的耙子。 玩。

您假设指向切片的指针将在迭代时自动取消引用。

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

从有效出发:

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.

在内部,切片是由

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

这种结构非常小,使指针无用。

  • 澄清:指向切片的指针有一种用途:如果程序的多个部分需要共享同一切片,则对切片本身的修改会反映在程序的其他部分中(例如,如果从切片中删除元素,通过执行a = append(a [:i],a [i + 1:] ...)应该反映在其他数据结构持有的切片中)。但是,这很少是您想要的,并且没有锁定也不是线程安全的。
  • 我花了几个小时进行调试,因为我一直试图变得太聪明了。我有个函数正在返回[] *结构,因为我不想复制内存。当构建返回结果时,这对我的代码造成了严重的破坏,因为我的片中的每个指针都指向每个循环迭代器的内存地址,这意味着我最终返回的片中有10个指针全部指向相同的结构。当我从方程式中取出指针并在每次循环后返回一个正常切片时,一切工作都很好。

从有效执行:

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

您尝试遍历指向单个值的切片的指针,因此无法进行收集。

将参数更改为populateClassRelationships作为切片,而不是指向切片的指针。或者您可以取消引用指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)
    }
}
  • 不,它不必是一个指针,我只是想使其工作。
  • 您能告诉我如何在没有指针的情况下进行操作吗?我将关键部分复制到play.golang.org/p/KonrOk3bp-
  • 第19行出现问题。
  • 我以为,如果您在调用populateClassRelationships时没有将指针传递给数组,那么您只是填充一个副本,而在ClassesForLastNDays中返回时,原始副本将为空白
  • 现在获取不能使用&classes(类型** [] entities.Class)作为函数参数中的[] entities.Class类型

如果需要从* slice中提取单个元素,则必须先取消引用它,例如:(*slice)[0]。在意识到这一点之前,我用头撞了*slice[0]了大约6个小时。它与操作顺序有关,与IMO无关,这不是一个非常好的结果。

我最终写了一些指针接收器方法来进行就地修改,例如追加和弹出,在我看来,这是一种合理的方式-可以在以下示例中找到示例:

  • @HassaanSalik-感谢您的支持,让我过去了,我不再这样做了。切片已经是指针类型,因此通常不应有任何理由需要*slice-自从重构了此代码以来,Id建议您寻找机会做同样的事情。

您可以取消引用指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)
    }
}

我还更改了range子句,因为我不认为您正在修改classes