Best practice for unions in Go

前端 未结 4 1513
一个人的身影
一个人的身影 2021-01-17 11:11

Go has no unions. But unions are necessary in many places. XML makes excessive use of unions or choice types. I tried to find out, which is the preferred way to work around

相关标签:
4条回答
  • 2021-01-17 11:32

    I am not sure to understand your issue. The 'easy' way to do it would be like the encoding/xml package with interface{}. If you do not want to use interfaces, then you can do something like you did. However, as you stated, Go is a typed language and therefore should be use for typed needs. If you have a structured XML, Go can be a good fit, but you need to write your schema. If you want a variadic schema (one given field can have multiple types), then you might be better off with an non-typed language.

    Very useful tool for json that could easily rewritten for xml: http://mholt.github.io/json-to-go/

    You give a json input and it gives you the exact Go struct. You can have multiple types, but you need to know what field has what type. If you don't, you need to use the reflection and indeed you loose a lot of the interest of Go.

    0 讨论(0)
  • 2021-01-17 11:32

    TL;DR You don't need a union, interface{} solves this better.

    Unions in C are used to access special memory/hardware. They also subvert the type system. Go does not have the language primitives access special memory/hardware, it also shunned volatile and bit-fields for the same reason.

    In C/C++ unions can also be used for really low level optimization / bit packing. The trade off: sacrifice the type system and increase complexity in favor of saving some bits. This of course comes with all the warnings about optimizations.

    Imagine Go had a native union type. How would the code be better? Rewrite the code with this:

    // pretend this struct was a union
    type MiscUnion struct {
      c *Comment
      pi *ProcessingInstruction
      ws *WhiteSpace
    }
    

    Even with a builtin union accessing the members of MiscUnion requires a runtime check of some kind. So using an interface is no worse off. Arguably the interface is superior as the runtime type checking is builtin (impossible to get wrong) and has really nice syntax for dealing with it.

    One advantage of a union type is static type check to make sure only proper concrete types where put in a Misc. The Go way of solving this is "New..." functions, e.g. MiscComment, MiscProcessingInstruction, MiscWhiteSpace.

    Here is a cleaned up example using interface{} and New* functions: http://play.golang.org/p/d5bC8mZAB_

    0 讨论(0)
  • 2021-01-17 11:39

    As it seems that you're asking because you want type safety, I would firstly argue that your initial solution is already unsafe as you have

    func (m Misc) Comment () *Comment {
        return m.value.(*Comment)
    }
    

    which will panic if you haven't checked IsComment before. Therefore this solution has no benefits over a type switch as proposed by Volker.

    Since you want to group your code you could write a function that determines what a Misc element is:

    func IsMisc(v {}interface) bool {
        switch v.(type) {
            case Comment: return true
            // ...
        }
    }
    

    That, however, would bring you no compiler type checking either.

    If you want to be able to identify something as Misc by the compiler then you should consider creating an interface that marks something as Misc:

    type Misc interface {
        ImplementsMisc()
    }
    
    type Comment Chars
    func (c Comment) ImplementsMisc() {}
    
    type ProcessingInstruction
    func (p ProcessingInstruction) ImplementsMisc() {}
    

    This way you could write functions that are only handling misc. objects and get decide later what you really want to handle (Comments, instructions, ...) in these functions.

    If you want to mimic unions then the way you wrote it is the way to go as far as I know.

    0 讨论(0)
  • 2021-01-17 11:52

    I think this amount of code might be reduced, e.g. I personally do not think that safeguarding type Misc against containing "illegal" stuff is really helpful: A simple type Misc interface{} would do, or?

    With that you spare the constructors and all the Is{Comment,ProcessingInstruction,WhiteSpace} methods boil down to a type switch

    switch m := misc.(type) {
        Comment: fmt.Println(m)
        ... 
        default: panic()
    }
    

    Thats what package encoding/xml does with Token.

    0 讨论(0)
提交回复
热议问题