这个多个case条件在类型切换断言中有什么问题?

huangapple go评论83阅读模式
英文:

Go: what is wrong with this multiple case conditions in type switch assertions?

问题

我有一个大致如下的代码:

func BulkInsert(docs interface{}) {
    switch data := docs.(type) {
        case map[string]*model.SnapshotByConv, map[string]*model.UserSnapshotsMap:
            for ver, _ := range data {
                // 其他逻辑...
            }
        case map[int64]map[string]*model.Delta:
            for ver, _ := range data {
                // 其他逻辑...
            }
    }
}

然后在编译时我遇到了错误:cannot range over data (type interface {}),这个错误是由第一个 range 引起的。

如果我移除第一个 case 中的多个类型,也就是将其改为 case map[string]*model.SnapshotByConv:

那么编译错误就消失了,这很奇怪,因为我需要对这两种类型执行完全相同的逻辑,所以为什么我不能将它们放在同一个 case 中呢?

请帮忙解决,谢谢。

我认为这里的情况与这个已经有答案的问题不同:https://stackoverflow.com/questions/40575033/golang-multiple-case-in-type-switch,那个问题是试图找到一种方法来识别类型,但这里我只是不想识别特定的类型,只是简单地运行一些逻辑,我找不到一种优雅的方法来实现这一点。

英文:

I have the code generally like this:

func BulkInsert(docs interface{}) {
    switch data := docs.(type) {
        case map[string] *model.SnapshotByConv, map[string] *model.UserSnapshotsMap:
            for ver, _ := range data {
                // other logics ...
            }
        case map[int64] map[string] *model.Delta:
            for ver, _ := range data {
                // other logics ...
            }
    }
}

Then when compile I got the error:
cannot range over data (type interface {}), it is raised by the first range.

If I remove the multiple types in the first case, which means leave it as case map[string] *model.SnapshotByConv:

Then the compile error is gone, it is wried, I need to proceed the exactly same logics on those two types, so why I cannot put them in a same case?

Pls help, thanks.

I think the case here is not same with the one already had answer here: https://stackoverflow.com/questions/40575033/golang-multiple-case-in-type-switch, which is trying to find a way to identify the types, but here is I just don't want to identify certain types and simply run some logics on it, I cannot find a elegant way to do this.

答案1

得分: 3

如果案例列举了多种类型(就像你的示例中那样),变量的类型将不会被缩小,所以在你的情况下它将是interface{}。这就是你链接的问题所说的,没有办法绕过它。

要为两种不同类型的情况分派一些通用代码,你需要在中间引入一个接口。(如果Go在某个时候引入了泛型,这可能会改变,但从目前的情况来看,这可能不会很快发生,甚至可能永远不会发生。)有几种方法可以做到这一点,以下是三种方法:

1 - 为你的映射定义类型,并添加一个公共接口

不直接使用映射,而是定义映射类型,并为其添加函数。

type SnapshotMap map[string]*model.SnapshotByConv
type UserSnapshotMap map[string]*model.UserSnapshotsMap

func (sm *SnapshotMap) DoLogic() {/*...*/}
func (usm *UserSnapshotMap) DoLogic() {/*...*/}

type LogicDoer interface {
    DoLogic()
}

// ...

switch data := docs.(type) {
case LogicDoer:
    data.DoLogic()
// ...
}

2 - 使映射持有一个公共接口

不要创建指针的映射,而是创建你的快照类型共享的接口的映射。

type Snapshot interface {
    LogicCommonToSnapshots()
}
var doc interface{}

// 在其他地方,这将发生而不是创建map[string]*Something
doc = make(map[string]Snapshot)

// ...

switch data := doc.(type) {
case map[string]Snapshot:
    for ver, _ := range data {
        ver.LogicCommonToSnapshots()
    }
// ...
}

3 - 将逻辑拆分出来,并使其回调到一个公共接口

将switch case拆分为两个单独的case,但将逻辑拆分为一个可以操作任何快照类型的函数。这样,你就不必重复逻辑。

type Snapshot interface {
    LogicCommonToSnapshots()
}

func ComplexLogic(s Snapshot) {/*...*/}

switch data := doc.(type) {
case map[string]*model.SnapshotByConv:
    for ver, _ := range data
        ComplexLogic(ver)
    }
case map[string]*model.UserSnapshotsMap:
    for ver, _ := range data {
        ComplexLogic(ver)
    }
// ...
}

请注意,在这三种情况下,都有一个接口将公共逻辑与逻辑作用的对象分离开来。为了实现你所描述的内容,你必须必然在逻辑和对象之间引入动态绑定,可以通过接口或通过创建捕获对象并传递给逻辑的匿名函数来实现。

英文:

If the case enumerates multiple types (like it does in your example), the variable's type won't be narrowed, so in your case it will be interface{}. This is what the question you link to says, and there is no way around it.

To dispatch some generic code for two different type cases, you will need to introduce an interface somewhere inbetween. (This might change if Go at some point introduces generics, but from the looks of it, that won't happen soon if ever.) There are a few ways you could do this, here are three:

1 - Define types for your maps, and add a common interface

Don't use the maps directly, but define types that are maps, and add functions to them.

type SnapshotMap map[string]*model.SnapshotByConv
type UserSnapshotMap map[string]*model.UserSnapshotsMap

func (sm *SnapshotMap) DoLogic() {/*...*/}
func (usm *UserSnapshotMap) DoLogic() {/*...*/}

type LogicDoer interface {
    DoLogic()
}

// ...

switch data := docs.(type) {
case LogicDoer:
    data.DoLogic()
// ...
}

2 - Make the maps hold a common interface

Don't create maps of pointers, but maps of interfaces that your snapshot types share.

type Snapshot interface {
    LogicCommonToSnapshots()
}
var doc interface{}

// Somewhere else, this happens rather than a creating map[string]*Something
doc = make(map[string]Snapshot)

// ...

switch data := doc.(type) {
case map[string]Snapshot:
    for ver, _ := range data {
        ver.LogicCommonToSnapshots()
    }
// ...
}

3 - Break out the logic and make it call back onto a common interface

Split the switch case into two separate cases, but break out the logic into a function that can operate on either snapshot type. That way, you won't have to duplicate the logic.

type Snapshot interface {
    LogicCommonToSnapshots()
}

func ComplexLogic(s Snapshot) {/*...*/}

switch data := doc.(type) {
case map[string] *model.SnapshotByConv:
    for ver, _ := range data
        ComplexLogic(ver)
    }
case map[string] *model.UserSnapshotsMap:
    for ver, _ := range data {
        ComplexLogic(ver)
    }
// ...
}

Notice that in all three cases, there is an interface that separates the common logic from the object that logic acts on. You must necessarily introduce a dynamic binding between the logic and the object to accomplish what you describe, either through interfaces or by e.g. creating an anonymous function that captures the object gets passed to the logic.

huangapple
  • 本文由 发表于 2017年3月25日 22:13:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/43017440.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定