When should a type be a struct containing another

2019-04-13 01:31发布

I'm currently learning Go by doing the rosalind problems (basically a bunch of bioinformatics related code katas).

I'm currently representing a DNA strand with a type:

type DNAStrand struct {
    dna byte[]
}

My initial reason was to encapsulate the byte slice so I would know it only contained bytes representing the nucleotides: 'A', 'C', 'G', 'T'. I realized that this was obviously not guarateed since I could simply do:

DNAStrand{[]byte("foo bar")}

And there is no longer any guarantee that my dna strand contains a byte array with only elements from those four bytes.

Since my struct only contains a byte array is it better/more ideomatic to do:

type DNAStrand []byte

Or is it better to let the type contain the dna strand? Are there any rules of thumb for when to use either of the two approaches?

标签: types struct go
3条回答
再贱就再见
2楼-- · 2019-04-13 01:46

I'd use type DNAStrand []byte because it's simple, and because I can use regexps on it. I'd probably use an initialisation function that checks that every byte is in ACGT though.

var validDNAStrandPat = regexp.MustCompile("[ACTG]*")

func DNAStrandForString(s string) DNAStrand {
    if !validDNAStrandPat.Match(s) {
        panic("Invalid DNA Strand.")
    }
    return DNAStrand([]byte(s))
}
查看更多
孤傲高冷的网名
3楼-- · 2019-04-13 01:54

Struct with zero fields are handy. Structs with many fields are handy even more. Structs with exactly one field are a bit special and I can't think of a reasonably "good" case where to use them - even though they are seen regularly "in the wild". I, for one, don't use them.

Anyway, if you really really need tighter/bulletproof safety about the DNAStrand slice content - then it is possible to to use the single field struct and define an argument checking setter method for this/such named type.

In that case, if the definition is later used from some other package, there's no way, modulo using package unsafe, to circumvent the checks and get a result equivalent to your DNAStrand{[]byte("foo bar")} example.

查看更多
时光不老,我们不散
4楼-- · 2019-04-13 01:54

Taking your specific example I would probably do something like this:

type neucleotide char // unexported type users can't construct their own.

type DNAStrand []neucleotide // because users can't construct their own
                             // nucleotides they also can't construct their
                             // own DNAStrands.

const (
  // These are exported values so they can use these nucleotides to construct a
  // DNAStrand with.
  A nucleotide = 'A'
  C nucleotide = 'C'
  G nudleotide = 'G'
  T nucleotide = 'T'
)

// This function allows them to actually construct a DNAstrand with a list of
//  nucleotides from the constants above.
func New(nts ...nucleotide) DNAStrand {
    return nts
}

Since the nucleotide type is not exported users can't construct their own. You provide the only allowed instances of them in the exported consts so no user can provide their own new nucleotides.

查看更多
登录 后发表回答