Structs give the caller so many immediate benefits:

   - If there's internal state that clients shouldn't muck around with, 
   then (a) keep a field private, or (b) keep a field private and make it a 
   pointer to state (such as mutexes).
   - Structs can be copied directly.
   - Set and get don't require more methods

On that basis alone, even for simple structures, code saves at least three 
methods in an interface (one "set", one "get", and one "Clone"). Less code 
to maintain all around.

On top of that, when writing test code, mocking out someone's else's API, 
returning structs results in more maintainable code. Why? If the API 
returns a structure, then the mock must define an interface that captures 
precisely what is needed from the structure. Whereas, if an API returns an 
interface, then the mock implementation may not bother with defining a 
subset interface, thus writing a much larger mock implementation than 
needed. Again, less code to maintain.

In the end, Go gives you the power to choose either approach, depending on 
the situation. From my experience with Go code, the default position to 
return structures (well, typically pointers to structures) ends up being 
less code, and easier to understand over time. It also forces the design of 
libraries to be open about the contents of structures, which usually 
results in easier to understand APIs.

Eric.

On Wednesday, March 1, 2017 at 8:01:14 PM UTC-8, Henry wrote:
>
> Hi, 
>
> I am trying to come up with a general guideline about returning struct vs 
> returning interface. I have used both approaches. Despite the common wisdom 
> is to return struct, I believe that returning interface is generally 
> superior to returning struct. Here are my arguments for returning interface:
>
> First, you may not want your data type to be copied by value. The data 
> type may contain mutexes, references to other data types, etc. If you 
> are returning struct, you may need to put comments in your code to prevent 
> people from copying your data type by value. Note that this isn't ideal 
> because an implementation is supposed to be a black box and the user should 
> not need to know whether there is a mutex, etc. inside the struct.  
>
> //DO NOT COPY. I MEAN IT!
> type VolatileDataType struct{
>    mtx sync.RWMutex
>    data  map[string]OtherData
>    //etc..
> }
>
> With the above comment, while people may remember not to do this:
> myCopy := myVolatileDataType //myVolatileDataType is of type 
> VolatileDataType
>
>
> It is harder to prevent people from doing these:
> func DoWork(data VolatileDataType){
>    //...
> }
> //or this 
> func DoWork() VolatileDataType{
>    //...
> }
> //or this
> myChannel := make (chan VolatileDataType)
>
>
> If you are returning interface, you avoid all these complications in a 
> somewhat simpler way:
> type VolatileDataType interface{
>    Clone() VolatileDataType
>    //...
> }
>
> type volatileImpl struct{
>    mtx  sync.RWMutex 
>    data map[string]OtherData
>    //etc...
> }
>
> func NewVolatileDataType() VolatileDataType{
>    return &volatileImpl{
>       //etc...
>    }
> }
>
>
> By returning interface, people can do all the followings without any 
> problem. You are actually passing around references to the actual object, 
> and you will have to explicitly call its copy method if you need a copy.
> myPtr := myVolatileDataType //in the case of passing the actual data type 
> around 
> //or
> myCopy := myVolatileDataType.Clone() //if the data type supports copying
>
> func DoWork(data VolatileDataType){
>    //...
> }
>
> func DoWork() VolatileDataType{
>    //...
> }
>
> myChannel := make (chan VolatileDataType)
>
> In the case where your data type can be safely passed around by value, it 
> doesn't matter whether you return interface or struct. So there isn't 
> a problem in returning interface.
>
> Second, returning interface removes the need for dereferencing your data 
> type. It results in a cleaner syntax. It also allows better equality 
> comparison. Structs that contains map, etc. needs deeper equality 
> comparison.
> s1:= MethodStruct1() //func() MyStruct
> s2:= MethodStruct2() //func() *MyStruct
>
> if s1 != *s2{
>    //do something
> }
>
> i1:=MethodInterface1() //func() MyInterface
> i2:=MethodInterface2() //func() MyInterface
>
> if i1.Equal(i2){
>    //do something
> }
>
>
> Finally, even if you are returning interface, you can still accept a 
> subset of its interface (per Go's wisdom of accepting small interface).
> type VolatileDataType interface{
>    String() string
> }
>
> func NewVolatileDataType() VolatileDataType{
>    //...
> }
>
> func Print(target Stringer){
>   //...
> }
>
> Print(NewVolatileDataType()) //still works fine.
>
>
> I wonder whether it is correct to conclude that in general people should 
> return an interface, unless there is a real reason not to. Or better is "to 
> accept and return interfaces".
>
> Let me know what your experiences are regarding this matter.
>
> Thanks.
>
> Henry
>
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to