shorter way to say "mutex.Lock(); defer mutex.Unlock()"

4,564 views
Skip to first unread message

Tyler Breisacher

unread,
Oct 5, 2013, 4:35:38 PM10/5/13
to golan...@googlegroups.com
I noticed that the following pattern is fairly common at the beginning of a function (where m is a sync.Mutex, or more generally, a sync.Locker):

m.Lock()
defer m.Unlock()

It occurred to me that the way 'defer' works means you could instead do:

defer m.Lock().Unlock()

to save one line. You'd need to change m.Lock() to return itself: http://play.golang.org/p/Jke5TPaB84

Is it worth it? Or would it add too much confusion, while only saving one line?

Kevin Gillette

unread,
Oct 6, 2013, 2:23:27 AM10/6/13
to golan...@googlegroups.com
You may have noticed that many decisions have been made between readability (often via syntax normalization) and conciseness in the design of Go, often favoring the former. For example, the following is not valid Go, although it would have saved a couple lines:

if somethingBad() log.Fatalln("bad")
else log.Println("good")

As a community, we're generally not too concerned with lines-of-code savings solely for its own purpose, especially if there's any tradeoff.

This is a neat trick, however, but it will be confusing to many gophers (I had to check it myself), since it's exceptionally rare in practice for a defer to have more than one call involved in an expression; in this case, the confusion centers around whether only the right-most call is deferred (which is what actually happens), or if the entire sub-expression beginning with the first call is deferred.

Oleku Konko

unread,
Oct 7, 2013, 3:44:22 AM10/7/13
to golan...@googlegroups.com
I really don't see it as a problem ... try embedding http://play.golang.org/p/QXAckbVCvE 

Robert Johnstone

unread,
Oct 7, 2013, 9:35:43 AM10/7/13
to golan...@googlegroups.com
That would be incorrect. It would defeat the purpose of the using a mutex to defer the lock until the end of the function.

chris dollin

unread,
Oct 7, 2013, 9:40:07 AM10/7/13
to Robert Johnstone, golang-nuts
On 7 October 2013 14:35, Robert Johnstone <r.w.jo...@gmail.com> wrote:
That would be incorrect. It would defeat the purpose of the using a mutex to defer the lock until the end of the function.

(a) why? (b) if so, the original original code, viz

    m.Lock()
    defer m.Unlock()

would suffer from the same problem. If there were a problem. And
not, if there were not.



On Saturday, 5 October 2013 16:35:38 UTC-4, Tyler Breisacher wrote:
It occurred to me that the way 'defer' works means you could instead do:

defer m.Lock().Unlock()


--
Chris "allusive" Dollin

Robert Johnstone

unread,
Oct 7, 2013, 1:00:35 PM10/7/13
to golan...@googlegroups.com, Robert Johnstone, ehog....@googlemail.com
I just wrote a quick example in play.golang.org (http://play.golang.org/p/W3IyZqz8MQ), and the original one-liner does work.  Oops.  I had read the line to indicate that both the call to Lock and the call to Unlock would be part of the deferred expression, but that is clearly wrong.  A closer read of the specification seems to indicate that the call to Lock is run immediately to prepare the parameters for the deferred call to Unlock.  Sorry for the erroneous post.

minux

unread,
Oct 7, 2013, 1:06:37 PM10/7/13
to Robert Johnstone, golang-nuts, chris dollin
On Mon, Oct 7, 2013 at 1:00 PM, Robert Johnstone <r.w.jo...@gmail.com> wrote:
I just wrote a quick example in play.golang.org (http://play.golang.org/p/W3IyZqz8MQ), and the original one-liner does work.  Oops.  I had read the line to indicate that both the call to Lock and the call to Unlock would be part of the deferred expression, but that is clearly wrong.  A closer read of the specification seems to indicate that the call to Lock is run immediately to prepare the parameters for the deferred call to Unlock.  Sorry for the erroneous post.
Just FYI, a similar usage of defer is documented in the Effective Go article:

and the example program in the Go playground: http://play.golang.org/p/fmve5osMc6 

Kevin Gillette

unread,
Oct 7, 2013, 2:34:51 PM10/7/13
to golan...@googlegroups.com, Robert Johnstone, ehog....@googlemail.com
On Monday, October 7, 2013 11:00:35 AM UTC-6, Robert Johnstone wrote:
Oops. I had read the line to indicate that both the call to Lock and the call to Unlock would be part of the deferred expression, but that is clearly wrong.

Exactly, emphasis on the "oops" effect that this style would lead to if frequently used. Go avoids other oopses by:
  • requiring braces around even single statement control structures ("well in my defense, you can see that the second line is indented too")
  • using explicit error handling ("I didn't know that function could raise that exception. Too bad: this daemon was running for almost 6 months since the last time something like this happened")
  • avoiding inheritance ("gee, I just spent 2 days arranging my inheritance hierarchy, but am still trying to reconcile theory with practice in deciding whether square should subclass rect, or vice versa")
  • not providing methods on builtin types (lookup "monkey-patching")
  • not supporting implicit conversions (this could especially cause issues in a type-inferenced language like Go)
  • not providing user-configurable operator overloading ("why does my program cause a network spike and then hang whenever I multiply these two MyInt variables?")
The above is hardly an exhaustive list, and only covers language aspects, not convention (which is what the single line lock/unlock defer touches on).

Jan Mercl

unread,
Oct 7, 2013, 2:57:36 PM10/7/13
to Kevin Gillette, golang-nuts, Robert Johnstone, chris dollin
On Mon, Oct 7, 2013 at 8:34 PM, Kevin Gillette
<extempor...@gmail.com> wrote:
> On Monday, October 7, 2013 11:00:35 AM UTC-6, Robert Johnstone wrote:
>>
>> Oops. I had read the line to indicate that both the call to Lock and the
>> call to Unlock would be part of the deferred expression, but that is clearly
>> wrong.
>
>
> Exactly, emphasis on the "oops" effect that this style would lead to if
> frequently used. Go avoids other oopses by:
>
> requiring braces around even single statement control structures ("well in
> my defense, you can see that the second line is indented too")

if expr1 { stmt } else if expr2 { stmt2 } // control "structure" w/o
braces exist

> avoiding inheritance ("gee, I just spent 2 days arranging my inheritance
> hierarchy, but am still trying to reconcile theory with practice in deciding
> whether square should subclass rect, or vice versa")

Go has complete OOP inheritance. Show me where structural inheritance
is a necessary feature of "OOP". Hint: It was originally defined by
the means of message passing. That's clearly a behavioral concept.

> not supporting implicit conversions (this could especially cause issues in a
> type-inferenced language like Go)

Go uses implicit conversions widely and probably more often than you
realize. Have you recenlty fmt.Printf'ed? ;-)

> not providing user-configurable operator overloading ("why does my program
> cause a network spike and then hang whenever I multiply these two MyInt
> variables?")

-j

Thomas Bushnell, BSG

unread,
Oct 7, 2013, 3:21:52 PM10/7/13
to Jan Mercl, Kevin Gillette, golang-nuts, Robert Johnstone, chris dollin
On Mon, Oct 7, 2013 at 11:57 AM, Jan Mercl <0xj...@gmail.com> wrote:

Go has complete OOP inheritance. Show me where structural inheritance
is a necessary feature of "OOP". Hint: It was originally defined by
the means of message passing. That's clearly a behavioral concept.

Sorry, but it doesn't.  The key point is that when a method is defined for a superclass but not a subclass, and it sends a message to "self", the message is looked up in the subclass first. That doesn't work in Go.

Thomas

Kevin Gillette

unread,
Oct 7, 2013, 4:04:23 PM10/7/13
to golan...@googlegroups.com, Kevin Gillette, Robert Johnstone, chris dollin
On Monday, October 7, 2013 12:57:36 PM UTC-6, Jan Mercl wrote:
if expr1 { stmt } else if expr2 { stmt2 } // control "structure" w/o braces exist

I presume you're referring to "else" as being braceless in this case?
 
Go has complete OOP inheritance. Show me where structural inheritance
is a necessary feature of "OOP".

The target audience of my remark was those who are not already seasoned Go developers. More often than not, said audience will not be able to distinguish between "inheritance" and "structural inheritance".

Go uses implicit conversions widely and probably more often than you 
realize. Have you recenlty fmt.Printf'ed? ;-)

You're correct: I should have specifically said "numeric conversions." Where it can be a serious problem, such as in implicit conversions between `type (miles float64; kilometers float64)`, does not apply to boxing/unboxing, since the underlying type is not changed in that case.

Tyler Breisacher

unread,
Oct 8, 2013, 12:34:55 AM10/8/13
to golan...@googlegroups.com, Robert Johnstone, ehog....@googlemail.com
"I had read the line to indicate that both the call to Lock and the call to Unlock would be part of the deferred expression, but that is clearly wrong."

I think this is enough to convince me that it's a bad idea. If you read it wrong, it's likely a lot of people will, and as far as I can tell Go is designed to be as gotcha-free as possible, even if means being a bit less concise. One extra line is a small price to pay for the code being totally unambiguous, even to someone who hasn't thought carefully about all the edge cases of defer statements.

Jan Mercl

unread,
Oct 8, 2013, 4:09:06 AM10/8/13
to Tyler Breisacher, golang-nuts
Perhaps "better" confusing (JFF): http://play.golang.org/p/HT7uZvllfX

-j

Jan Mercl

unread,
Oct 8, 2013, 4:16:06 AM10/8/13
to Thomas Bushnell, BSG, Kevin Gillette, golang-nuts, Robert Johnstone, chris dollin
I'm not sure if I understood correctly the assignment:
http://play.golang.org/p/rFmxCBmcA0

-j

adam...@gmail.com

unread,
Mar 27, 2015, 8:11:49 PM3/27/15
to golan...@googlegroups.com, r.w.jo...@gmail.com, ehog....@googlemail.com
 

Exactly, emphasis on the "oops" effect that this style would lead to if frequently used. 

Just stumbling across this quite old thread, but I have to say this is a nifty idea, and a useful feature of the language. Why not use it? If this style were frequently used, I'd argue it would simply become quickly understood and commonplace. I would even argue that this nuance of defer should be commonly understood. 
Reply all
Reply to author
Forward
0 new messages