【问题标题】:Golang : type conversion between slices of structsGolang:结构切片之间的类型转换
【发布时间】:2014-07-08 21:53:57
【问题描述】:

这个问题跟随another question of mine

在以下测试代码中尝试将 res 转换为 ListSociete 时,我并没有完全理解有什么问题:

import (
    "errors"
    "fmt"
    "github.com/jmcvetta/neoism"
)

type Societe struct {
    Name string
}

type ListSociete []Societe

func loadListSociete(name string) (ListSociete, error) {
    db, err := neoism.Connect("http://localhost:7474/db/data")
    if err != nil {
        return nil, err
    }
    res := []struct {
        Name string `json:"a.name"`
    }{}
    cq := neoism.CypherQuery{
        Statement: `
            MATCH (a:Societe)
            WHERE a.name = {name}
            RETURN a.name
            `,
        Parameters: neoism.Props{"name": name},
        Result:     &res,
    }
    db.Cypher(&cq)
    if len(res) == 0 {
        return nil, errors.New("Page duz not exists")
    }
    r := res[0]
    return ListSociete(res), nil
}

[]struct{Name string}[]struct{Name string json:"a.name" } 不同吗?

还是 ListSociete 与 []struct{Name string} 不同?

谢谢。

【问题讨论】:

  • json:"a.name" 添加到Societe 是否有效?使用res := Societe{}还有什么问题?
  • 错误是什么?输出?
  • 对不起,我忘记了。这是:cannot convert res (type []struct { Name string "json:\"a.name\"" }) to type ListSociete
  • @OneOfOne 做了一些测试,将 json 标签添加到 Societe 不起作用。 ListSociete 似乎与 []struct{Name string} 不同。
  • 在定义 res := Societe{} 时,这并不是一个真正的选择,因为我需要标签来从我的数据库查询中提取结果(此外,Societe 只是一个结构,而 res 是一个结构的片段)

标签: struct go type-conversion


【解决方案1】:

您目前正在处理两种不同的类型:

type Societe struct {
    Name string
}

还有匿名者:

struct {
    Name string `json:"a.name"`
}

如果没有标签,这两个将是相同的。 Go Specifications 声明(我的重点):

如果两个结构类型具有相同的字段序列,并且如果 对应的字段具有相同的名称和相同的类型,和相同的标签。 两个匿名字段被认为具有相同的名称。小写字段名称 来自不同的包总是不同的。

因此,您无法在两者之间进行简单的转换。此外,您正在转换这两种类型的 slices 的事实使转换存在问题。我可以为您看到两个选项:

通过迭代复制:

这是安全且推荐的解决方案,但也更加冗长和缓慢。

ls := make(ListSociete, len(res))
for i := 0; i < len(res); i++ { 
    ls[i].Name = res[i].Name
}
return ls, nil

不安全的转换:

由于这两种类型具有相同的底层数据结构,因此可能会进行不安全的转换。
不过,这可能会在您稍后出现。请注意!

return *(*ListSociete)(unsafe.Pointer(&res)), nil

游乐场示例: http://play.golang.org/p/lfk7qBp2Gb

【讨论】:

  • 虽然您可以在仅标签不同的结构之间进行转换,但您不能在此类结构的切片之间进行转换。为什么?见play.golang.org/p/RSRoPnvXXU7
  • 这似乎是一个语言设计缺陷。您需要复制切片内容 - 主要的性能损失或使用不安全的内存指针。这两者都违反了 Go 的指导原则。
  • “您需要复制切片内容”嗯.. 空切片呢?如果需要,将空的结构切片复制到空的接口切片
  • 这个答案在当时是正确的,但是从 Go 1.8(2017 年 2 月)开始,这个限制已经被移除,因此现在可以将两个仅标签不同的结构从一种类型转换为另一种类型,允许这种分配。但是,转换仍然必须是明确的:它们仍然被视为不同的类型。见beta.golang.org/doc/go1.8#language
  • (扩展我之前的评论:您仍然不能一次分配不同结构类型的整个切片,但您现在可以一次分配整个“兼容”结构,而不必复制单个元素每个结构)
【解决方案2】:

所以,经过一些测试,这是我发现的:

这样定义的 ListSociete...

type Societe struct {
    Name string `json:"a.name"`
}

type ListSociete []Societe

与此不同:

type ListSociete []struct {
    Name string `json:"a.name"`
}

第二个解决方案有效,而第一个无效。

所以我认为真的没有办法在具有不同标签的类型之间进行转换(直接而不编写显式循环)?

在那种情况下,我肯定会使用循环,因为直接在类型中使用标签(参见上面的第二个解决方案)会使我的代码不可读和不可重用,而且我真的不知道我会搞乱使用不安全的转换方法。所以感谢您确认不同的标签制作了不同的类型。

【讨论】:

  • Loop whiwh 工作得很好,顺便说一句。真的慢很多吗? (执行,我的意思)
  • 这取决于列表的大小。不安全的转换是 O(1) 操作,而循环是 O(N)。最有可能的是,迭代对于大多数用途来说足够快。奇怪的是它对你不起作用。它在 playground example 中运行良好
  • 那我一定是在什么地方做错了。我会尝试再次完成这项工作。
猜你喜欢
  • 2015-08-31
  • 1970-01-01
  • 2018-01-04
  • 2019-03-09
  • 2012-08-09
  • 1970-01-01
  • 1970-01-01
  • 2020-09-12
  • 1970-01-01
相关资源
最近更新 更多