Megidd
Megidd

Reputation: 7961

Conditional/optional fields in struct type

I have a Node struct type like this:

package tree

// Enum for node category
type Level int32
const (
    Leaf Level = iota + 1
    Branch
    Root
)

type Node struct {
    Children []*Node
    Parent   *Node
    X        float32
    Y        float32
    Z        float32
    Dim      float32
    Category Level
    LeafPenetration float32 // Needed only if category is "Leaf"
    RootPadDim float32      // Needed only if category is "Root"
}

I have two fields of Node which are optional and only needed depending upon category field:

    leafPenetration float32 // Needed only if category is "Leaf"
    rootPadDim float32      // Needed only if category is "Root"

Is the current Node implementation fine? What is the best practice for such optional/conditional fields inside struct types?

Upvotes: 4

Views: 12294

Answers (3)

Megidd
Megidd

Reputation: 7961

I ended up using a very simplified approach:

package tree

// Enum for node category
type Level int32
const (
    Leaf Level = iota + 1
    Branch
    Root
)

type Node struct {
    Category Level
    Parent   *Node
    X        float32
    Y        float32
    Z        float32
    Dim      float32

    RootT  float32 // Root thickness
    RootD  float32 // Root diameter
    RootBR float32 // Root bezel ratio of top-bottom, i.e. top D is larger by this much
    LeafP  float32 // Leaf penetration
}

func NewNode(cat Level, parent *Node, x, y, z, dim float32) *Node {
    n := &Node{
        Category: cat,
        Parent:   parent,
        X:        x,
        Y:        y,
        Z:        z,
        Dim:      dim,
    }
    switch n.Category {
    case Leaf:
        n.LeafP = float32(0.3)
    case Root:
        n.RootT = float32(2)
        n.RootD = float32(30)
        n.RootBR = float32(1.08)
    }
    return n
}

Upvotes: 0

Megidd
Megidd

Reputation: 7961

Is it a good idea to follow this guideline?

For example having this Node type:

package tree

// Enum for node category
type Level int32
const (
    Leaf Level = iota + 1
    Branch
    Root
)

type Node struct {
    Children []*Node
    Parent   *Node
    X        float32
    Y        float32
    Z        float32
    Dim      float32
    Category Level
    // https://github.com/uber-go/guide/blob/master/style.md#functional-options
    Opts []Option
}

Implementing options like this:

type options struct {
    penetration float32 // Needed only if category is "Leaf"
    base        float32 // Needed only if category is "Root"
}

type Option interface {
    apply(*options)
}

// penetration option
type penetrationOption float32

func (p penetrationOption) apply(opts *options) {
    opts.penetration = float32(p)
}

func WithPenetrationOption(p float32) Option {
    return penetrationOption(p)
}

// base option
type baseOption float32

func (b baseOption) apply(opts *options) {
    opts.base = float32(b)
}

func WithBaseOption(b float32) Option {
    return baseOption(b)
}

With the above approach, the implementation gets too complicated. However it can be expanded further in future. Not sure about its worthy-ness though.

Upvotes: -2

Grigoriy Mikhalkin
Grigoriy Mikhalkin

Reputation: 5573

By default, fields initialize to type's zero value -- in case of float32 it's 0. To avoid that, it's common to use pointers, for fields that are optional, like:

type Node struct {
    Children []*Node
    Parent   *Node
    X        float32
    Y        float32
    Z        float32
    Dim      float32
    Category Level

    // Optional fields
    LeafPenetration *float32  // Needed only if category is "Leaf"
    RootPadDim      *float32  // Needed only if category is "Root"
}

Pointer fields will be defaulted to nil.

Upvotes: 6

Related Questions