Golang:代码重复和类似的结构

What's an idiomatic way in Go to superclass similar (but not identical) data types in order to minimize code duplication? Trite example:

import "time"

type LinuxUtmp struct {
    ut_type uint16
    _       [2]byte
    ut_pid  uint32
    ut_line [32]byte
    ut_id   [4]byte
    ut_user [32]byte
    ut_host [256]byte
    exit_status [2]uint32
    tv_sec  uint32
    tv_usec uint32
    ...
}

func (l LinuxUtmp) User() string {
    return string(l.ut_user[:])
}

func (l LinuxUtmp) Time() time.Time {
    return time.Unix(int64(l.tv_sec), int64(l.tv_usec))
}

type BsdUtmp struct {
    ut_line [8]char
    ut_name [16]char
    ut_host [16]char
    ut_time uint32
}

func (b BsdUtmp) User() string {
    return string(b.ut_user[:])
}

func (b BsdUtmp) Time() time.Time {
    return time.Unix(int64(b.ut_time), 0)
}

Obviously there's more to it than that, but I'd love to be able to somehow superclass those so I only have to write and maintain one copy of particular functions. An interface seems to be the "right" way, but that leaves much to be desired (non-working example):

type Utmp interface {
    Time() time.Time
}

func User(u Utmp) string {
    return string(u.ut_user[:])
}

I've also considered embedding, but that seems a dead end too since Go is so strictly typed. Am I doomed to have multiple pieces of code that are identical in every way but the signature?

[edit]

Part of the complication is that I'm using encoding/binary.Read() to parse this data according to endianness (it's not just utmp records and not just Linux/BSD). To use that, the fields must be [exported] in the struct in the precise order they are on-disk. Hence I can't just embed the fields of another struct, as in some records they're in different order (and of differing sizes)

I don't understand your comment about embedding. Here is how I'd do it (using embedding):

package test

import "time"

type Utmp struct {
    // Common fields
}

func (u Utmp) User() {
    return string(l.ut_user[:])
}

type LinuxUtmp struct {
    Utmp
    // Linux specific fields
}

func (l LinuxUtmp) Time() time.Time {
    return time.Unix(int64(l.tv_sec), int64(l.tv_usec))
}

type BsdUtmp struct {
    Utmp
    // BSD specific fields
}

func (b BsdUtmp) Time() time.Time {
    return time.Unix(int64(b.ut_time), 0)
}

Any code importing the library can call User() method directly on LinuxUtmp and BsdUtmp objects directly as l.User() or b.User() without mentioning Utmp at all. You can even keep Utmp unexpected (as utmp) if you like.

Check out Effective Go for details.

You can even ensure that only the code meant for the relevant platform gets compiled in the binary, if you like. This blog has got some examples. In the interest of keeping things simple, I wouldn't bother going down that route if the platform specific code is not very big or there are other factors involved.

For the sake of completeness, here is the official go build doc.

If you have the problem that the types of some things are different, you could make the Time and User functions operate on an interface that wraps linux and bsd functionality.

If you don't like that, you can generate the code to avoid duplication.