For now it occurs to me in DTOs sometimes the need to embed types (another DTO) like the traits.
If you’re familiar with other languages that support inheritance (Java, Python, C++, C#, etc.). For example, I have a simple TempFile
implementation in some of my packages that is essentially this:
type TempFile struct {
*os.File
}
func (f TempFile) Close() error {
if err := f.try("close", func(f TempFile) error {
return f.File.Close()
}); err != nil {
return err
}
return f.try("remove", func(f TempFile) error {
return os.Remove(f.File.Name())
})
}
func (f TempFile) try(fn func(TempFile) error) error {
if err := fn(f); err != nil {
return fmt.Errorf(
"error attempting to %s file %v: %w",
what, f.Name(), err,
)
}
return nil
}
So essentially, my TempFile
type works the same as an *os.File
, except when Close
is called, it calls the “base” *os.File
’s Close
function and then removes the file. If *os.File
ever gets a new method, my TempFile
will “inherit” it.
However, this is the only situation that I can think of where I’ve done this. Usually I end up forgetting about embedded functions and I accidentally do something like this:
func (f TempFile) Read(p []byte) (n int, err error) {
// ...
nn, err := self.Read(p) // oops, I meant self.File.Read...
// ...
}
Which is pretty obvious and yet I manage to make the same mistake more than once. I guess I’m just not cut out for inheritance-like embedding
That reminded me of a line from Bitfield blog, in this article https://bitfieldconsulting.com/golang/commandments:
Don’t embed struct types so that they magically acquire invisible methods.
I think that’s what he’s referring to then. So it would very rarely be advisable to embed types, unless it is to build flat structs (without methods).
Yea, that’s a really good point. I’ve done that especially with structs with common fields that I (un)marshal JSON to/from.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.