【问题标题】:Golang cannot range over pointer to sliceGolang 无法覆盖指向切片的指针
【发布时间】:2014-01-22 08:43:44
【问题描述】:

尝试在切片指针上进行范围时,我不断收到此错误。

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
    }

【问题讨论】:

  • 切片已经是一种指针,没有理由指向它。
  • 我想要一片指针,所以我可以用PoulateClassRelationships func 填充它们
  • @dystroy 我认为您现在实际上有了最好的答案,因为您实际上已经找到了问题的根源。根据golang.org/doc/effective_go.html#slicesIf 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
  • 我曾经踩过同样的耙子。 Play.

标签: go


【解决方案1】:

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

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

来自Effective Go

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

在内部,切片是由

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

这个结构很小,导致指针没用。

【讨论】:

  • 澄清:指向切片的指针有一个用途:如果程序的多个部分需要共享同一个切片,那么对切片本身的修改会反映在程序的其他部分中(例如,如果通过执行 a = append(a[:i], a[i+1:]...) 从切片中删除元素应该反映在其他数据结构保存的切片中)。然而,这很少是你想要的,而且如果没有锁定,它就不是线程安全的。
  • 只是花了几个小时调试,因为我想太聪明了。我有一个返回 []*structs 的函数,因为我不想复制内存。在构建返回结果时,这对我在 for each 循环中的代码造成了严重破坏,因为我的切片中的每个指针都指向 for each 循环迭代器的内存地址,这意味着我最终返回的切片有 10 个指针都指向相同的结构。当我从等式中取出指针并在我的每个循环之后返回一个正常切片时,一切都运行良好。
【解决方案2】:

来自Effective Go

如果您正在循环遍历数组、切片、字符串或映射,或者正在读取 从一个通道,一个范围子句可以管理循环。

您正在尝试将 指针 迭代到一个切片,该切片是单个值,而不是集合,因此是不可能的。

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

【讨论】:

  • 不,它不一定是指针,我只是想让它工作。
  • 你能告诉我如何在没有指针的情况下做到这一点吗?我把关键部分复制到play.golang.org/p/KonrOk3bp-
  • 第19行有问题。
  • 我认为如果你在调用populateClassRelationships 时没有传递指向数组的指针,那么你只是在填充一个副本,而在ClassesForLastNDays 中返回时原件将是空白的
  • 现在无法在函数参数中使用 &classes (type **[]entities.Class) 作为类型 []entities.Class
【解决方案3】:

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

我最终编写了一些指针接收器方法来进行就地修改,例如追加和弹出,在我看来,这是一种合理的方式 - 可以在此处找到一个示例:https://play.golang.org/p/qZEYMcPHl4

【讨论】:

  • @HassaanSalik - 感谢您的支持,所以我会转告我不再这样做了。切片已经是指针类型,因此通常不需要*slice - 我已经重构了这段代码,我建议你寻找机会做同样的事情。
【解决方案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)
    }
}

我还更改了 range 子句,因为我认为您没有修改 classes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-15
    • 2017-08-11
    • 2019-07-18
    • 2015-05-01
    • 2016-12-28
    • 1970-01-01
    • 2021-05-03
    • 2021-09-01
    相关资源
    最近更新 更多