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