1

I'm building a type structure that represents some devices on my network. There are lots of types of these devices, such as:

type Foo struct  { ... }
type Bar struct  { ... }
type Spam struct { ... }

but they all share a few common fields, one of which is IP. I'd like to define Device as an interface so I can group these together logically

type Device interface {
    IP() string
    ...
}

type LogicalGrouping struct {
    Devices []Device
    ...
}

but I'm running into an error with overlapping names.

func (f *Foo) IP() string { return f.IP }  // same method name as field name

I can rename either the field or the method or both, but surely this must be a common use case -- grouping structs by the fields they have in common? Is there an idiomatic solution here?

030
  • 10,842
  • 12
  • 78
  • 123
Adam Smith
  • 52,157
  • 12
  • 73
  • 112
  • 9
    The usual solution here is to rename your struct fields so they are unexported. –  Jul 11 '18 at 19:36
  • 4
    You can't have a field and a method with the same name. What you want to name your fields and methods, so long as they are syntactically valid, is entirely up to you. – Adrian Jul 11 '18 at 20:11
  • Does this answer your question? [Interface naming convention Golang](https://stackoverflow.com/questions/38842457/interface-naming-convention-golang) – 030 Aug 13 '20 at 10:40

2 Answers2

2

The general rule of thumb is that interfaces define behaviours and fields define data.

The problem here is that you are using an interface to access data in fields. On it's own that's not necessarily a bad thing. But you might want to reconsider how to structure your code so that you don't get this collision.

The first question I would as is:

"Why do you need to export the field IP if the struct defines an IP() method that has access to it?"

You could make the struct field unexported i.e. ip not IP, and then in the place you are using struct.IP, use struct.IP() instead.

The second question is:

"If you have structs such as foo, bar, spam and they all share common fields, why not use composition to deduplicate the definition of those shared fields."

This would probably only work if you are really only after the data that's inside the struct, I would generally advocate for using interfaces in all cases.

If you have the following structs:

type Foo struct {
    IP string
    A string 
    B string
}

type Bar struct {
    IP string
    A string 
    B string
}

type Spam struct {
    IP string
    A string 
    B string
}

You could use a new struct to represent the fields they have in common, and anonymously compose that inside:

type Inner struct {
    IP string
    A string 
    B string
}

type Foo struct {
    Inner
}

type Bar struct {
    Inner
}

type Spam struct {
    Inner
}

In the function call where you were using struct.IP(), accept an Inner and call it with struct.Inner. You will still have access to struct.IP even though IP is defined inside Inner.

Zak
  • 5,515
  • 21
  • 33
  • 1
    the former method (`foo.ip` unexported, using `foo.IP()` for data access) is what I ended up using. My brain doesn't twist that way naturally, as I more readily consider `func (f *foo) IP() string` to be a method on the interface, not a method on the struct -- which is nonsense of course. – Adam Smith Jul 12 '18 at 18:55
0

You also may consider option to have ip field and GetIP & SetIP methods (or IP & SetIP accordingly to this doc) - it's clear, concise and simple.

cn007b
  • 16,596
  • 7
  • 59
  • 74
  • 3
    In general in go you would not create `GetFoo()` methods, instead opting for the simpler form of just `Foo()`. https://golang.org/doc/effective_go.html#Getters – Zak Jul 12 '18 at 16:51
  • @Zak Updated my answer. Thank you! – cn007b Jul 12 '18 at 18:12