Search code examples
listgoslicesequencecomposite-literals

golang: How to define a sequence of steps


I want to write a protocol test. My current idea is to define a series of steps. Each Step contains a Trigger and Expectation - e.g. trigger MsgA and expect MsgB.

type Series struct {
  Steps []Step
}

What I don't like about this, is that the Series is not explicit: I would have to either create the slice in sequence and then hope it never gets messed up, and rely on it, or sort it and then have to implement the sort funcs, which is utterly unnecessary.

What is a good design to formally define the Series, and in this way make it explicit?

The best idea I have for now is to create a map with int indexes:

type Series struct {
   Steps map[int]Step
}

series := Series{
   Steps: map[int]Step{
     0: Step{},
     1: Step{},
     2: Step{},
     ...
   }
}

Another option could be to build myself a linked list. go doesn't have an implementation (afaik). It feels odd to have to build one myself for this. But such a solution would also loose the explicit feature a bit: Basically you always have to navigate through the linked list to get the whole sequence (e.g. "what is step 4").


Solution

  • Instead of using a map, you can still use a slice and use a keyed composite literal:

    var steps = []Step{
        0: Step{},
        1: Step{},
        ...
    }
    

    This is detailed in Spec: Composite literals:

    CompositeLit  = LiteralType LiteralValue .
    LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
                    SliceType | MapType | TypeName [ TypeArgs ] .
    LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
    ElementList   = KeyedElement { "," KeyedElement } .
    KeyedElement  = [ Key ":" ] Element .
    Key           = FieldName | Expression | LiteralValue .
    FieldName     = identifier .
    Element       = Expression | LiteralValue .
    

    As you can see, KeyedElement may contain an optional Key ":" part.

    ... The key is interpreted as a field name for struct literals, an index for array and slice literals, and a key for map literals.

    Note that you can mix keyed and "unkeyed" elements, and they don't have to be contiguous. Also, if a key is missing in a slice literal, the index will be the previous index + 1.

    type Step struct{}
    
    var steps = []*Step{
        0:       &Step{},
        1:       &Step{},
        &Step{}, // implicit index: 2
        5:       &Step{},
    }
    fmt.Println(steps)
    

    Output of the above (try it on the Go Playground):

    [0x559008 0x559008 0x559008 <nil> <nil> 0x559008] 
    

    They keys may also be "out of order", meaning this is also valid:

    var steps = []*Step{
        5:       &Step{},
        0:       &Step{},
        1:       &Step{},
        &Step{}, // implicit index: 2
    }
    

    This outputs the same. Try it on the Go Playground.