Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedding a sync.Mutex is equally bad whether or not the type is exported #127

Closed
peterbourgon opened this issue Jul 6, 2021 · 3 comments · Fixed by #130
Closed

Embedding a sync.Mutex is equally bad whether or not the type is exported #127

peterbourgon opened this issue Jul 6, 2021 · 3 comments · Fixed by #130

Comments

@peterbourgon
Copy link

Unexported structs that use a mutex to protect fields of the struct may embed the mutex.

What makes unexported types special? 🙃

A mutex in a struct is almost always an implementation detail, used to provide concurrency-safety to the capabilities (methods) of the type. Embedding it allows callers to manipulate it directly, but that's an abstraction leak, breaks encapsulation, and is just an enormous maintenance risk, for what I hope are obvious reasons. Whatever callers are doing when they take and release the lock can equally well be expressed as a method, right?

Is there some other use case you have that motivates this exception?

@abhinav
Copy link
Collaborator

abhinav commented Jul 8, 2021

Hey, @peterbourgon, thanks for calling this out! We're in agreement that
unexported types aren't special here.

We left it there as an allowance for cases where engineers were embedding the
mutex solely to avoid typing foo.mu.Lock() and preferred typing
foo.Lock(). We felt that if they wanted to avoid those keystrokes, it was
okay only if the type is unexported, so that the Mutex field or the Lock
methods do not become part of the advertised public API of the type.

That said, we agree that maybe that nuance is difficult to communicate. We're
okay with switching the recommendation to advise against embedding mutexes.

@prashantv
Copy link
Contributor

This was also inline with mentions in official Go talks, e.g.,
https://talks.golang.org/2015/tricks.slide#15
https://talks.golang.org/2012/10things.slide#3 (see "Embedded lock")

The export modified was added to avoid the sync.Mutex methods from being part of the exported type's API (almost always the wrong thing).

The current guideline also clashes with #118 -- using exported methods as a hint that it's part of the API vs an internal method. An embedded mutex will export the Lock and Unlock methods, and typically the lock is encapsulated by the type's methods.

abhinav added a commit that referenced this issue Jul 8, 2021
Our guidance for mutexes suggested that it was okay to embed mutexes in
structs if the struct was unexported. This was left as an allowance for
"if you really want to do it, this is the only time it's okay", but that
nuance doesn't translate well to the style guide.

Advise against embedding mutexes completely to resolve any confusion
here.

Resolves #127
abhinav added a commit that referenced this issue Jul 8, 2021
Our guidance for mutexes suggested that it was okay to embed mutexes in
structs if the struct was unexported. This was left as an allowance for
"if you really want to do it, this is the only time it's okay", but that
nuance doesn't translate well to the style guide.

Advise against embedding mutexes completely to resolve any confusion
here.

Resolves #127
@peterbourgon
Copy link
Author

peterbourgon commented Jul 9, 2021

Nice! 😎

We left it there as an allowance for cases where engineers were embedding the mutex solely to avoid typing foo.mu.Lock() and preferred typing foo.Lock()

😧

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

3 participants