Most of the time, it won’t matter much. That’s why there is no consensus. Why doesn’t it matter?
For struct (or any type really) that the purpose is to represent a logic like AuthService
or ProductRepository
, or ProductListingHandler
, Logger
, their field usually don’t have any state, only a dependency. Here is an example of how does a struct called AuthService
might look like:
type CredentialAccessor interface {
GetUserByEmail(ctx context.Context, email string) (User, error)
}
type PasswordChecker interface {
HashPassword(password string) string
VerifyPassword(hashedPassword, password string) error
}
type AuthService struct{
credentialAccessor CredentialAccessor
passwordChecker PasswordChecker
}
func NewAuthService(
credentialAccessor CredentialAccessor,
passwordChecker PasswordChecker,
) AuthService { // or you can also return *AuthService
...
}
As you can see, all the field in the AuthService
doesn’t manage any state. The value of credentialAccessor
, nor passwordChecker
will never change. It most probably injected when the program run for the first time, and will stay there forever.
Because of this, you can return AuthService
(not *AuthService
) and expect it to just run. If you change it to *AuthService
it will also run correctly. Is there any performance overhead? Maybe there is, if you return *AuthService
, you might trigger a heap allocation, but it won’t matter. Because in the end of the day, you might cast AuthService
to an interface and trigger the allocation anyway. All the field in the AuthService
are also already wrapped as an interface, which might already trigger the allocation. On top of that, this allocation is so small, and only happen once. It’s so small, that you won’t even notice it.
If you need to maintain state, it would be wise to return a pointer, because you might not want to have a splitted state, where you pass the struct, modify it, but the caller don’t get the modification.
If your struct represent a data like User
or Response
, it’s a different story. You might want your struct to be immutable. In this case, returning pointer can be bad. Also, if it represent data, it will be allocated a lot and returning a pointer might escapes your struct to heap which can be bad for performance.
Now, in the above paragraphs, I talk about correctness and performance overhead. There is also argument about style. But style is very subjective. I believe you have your own style as well. In this case, just follow your style because it doesn’t matter for anybody else. If you work on a team, just agree on one style, and follow it.