Initialization of an unnamed embedded pointer field required to satisfy an interface

577 views
Skip to first unread message

Gbr

unread,
Aug 27, 2015, 6:48:42 PM8/27/15
to golang-nuts
Trying to construct a type with an embedded field where the type also implements an interface:


type X struct {
   
*sync.Mutex
   
Value  int
   
// other named fields
}

type Y
interface {
    sync
.Locker
   
// other method signatures properly implemented by X
}

/* Compile-time implementation prover */
var _ Y = X{}


The sync.Locker requires a pointer receiver - as other interfaces do.  This can be satisfied by X embedding the sync.Mutex as a pointer field.  The question is then initialization.

This works:

var _ Y = X{ new(sync.Mutex), 0}

but becomes clumsy as the number and complexity of fields in X increases (not to mention error prone as the order and type of fields in X are edited).

Naming *sync.Mutex in X does not work, as then Y is not implemented.

How then to initialize X or otherwise allow a direct embedding of sync.Mutex in X while implementing Y?

Thanks...

James Aguilar

unread,
Aug 27, 2015, 7:24:39 PM8/27/15
to golang-nuts
type X struct {
    Value int
    sync.Mutex
}

If I understand you correctly, you want to avoid having to type out the "new" for the mutex each time you use the type literal. This should work, shouldn't it?

Gbr

unread,
Aug 27, 2015, 7:35:12 PM8/27/15
to golang-nuts
No, unfortunately.  If you make sync.Mutex a direct embedded field (as opposed to a pointer field), you loose implementation of Y -- sync.Locker requires a pointer receiver.

James Aguilar

unread,
Aug 27, 2015, 7:41:57 PM8/27/15
to golang-nuts
Hrm. I think you're going to want to be passing around pointers of this thing anyway. Otherwise, you'll effectively have copies of the thing running around, all being protected by the same Mutex. Like so: http://play.golang.org/p/VvKXDXr5eQ

Gbr

unread,
Aug 27, 2015, 7:41:59 PM8/27/15
to golang-nuts


On Thursday, August 27, 2015 at 4:35:12 PM UTC-7, Gbr wrote:
No, unfortunately.  If you make sync.Mutex a direct embedded field (as opposed to a pointer field), you loose implementation of Y -- sync.Locker requires a pointer receiver.

Play ground:  example
 

James Aguilar

unread,
Aug 27, 2015, 7:48:20 PM8/27/15
to golang-nuts
Also: it's not that sync.Locker requires a pointer receiver. It's that (*sync.Mutex).Lock() and Unlock() do.

If you really want to have a pointer Mutex in objects that are meant to be copied, you're not going to be able to do it without the cumbersome new in each literal. The standard way to deal with this is to make a function that constructs instances of the object.

Tim K

unread,
Aug 27, 2015, 7:51:08 PM8/27/15
to golang-nuts
Maybe we are not on the same page, but I think this is what you really want:

Otherwise as it was pointed by James, you will have copies of X all sharing the same Mutex and I'm sure that's not what you want most of the time, if ever.

Gbr

unread,
Aug 27, 2015, 8:06:34 PM8/27/15
to golang-nuts
Yes, that is exactly what I want ->  the direct embedded Mutex without breaking the implementation of Y.  I thought I had tried that solution, but apparently not. 

Is there an intuitive way of explaining how/why that works - I hate to admit that I was almost to the point of just throwing *s and &s at it ;)

James Aguilar

unread,
Aug 27, 2015, 9:02:55 PM8/27/15
to golang-nuts
Yes. The Lock and Unlock functions require pointer receivers because it only makes sense to lock the original mutex, not a copy of it. When you have non-pointer receivers, the receiving object is not the original, it is a copy.

gerald.r...@gmail.com

unread,
Aug 28, 2015, 1:50:35 AM8/28/15
to golang-nuts
Yes, but that really isn't at the crux of the problem or the fix.

The strange looking thing is that by adding an '&' in the var assignment, sync.Locker in the method set Y suddenly accepts the structure X as implementing Y independent of whether the embedded field sync.Mutex is direct or pointer.

Requiring an embedded pointer field to satisfy the pointer-receiver requirement of a method set member seems logically correlated.  The obliteration of that correlation by a distant var assignment (spooky action at a distance) is, well, strange.  Guessing it has something to do with the heuristics of the type-inferencing system, but a guess is far shy of a comfortable understanding.

Jesse McNelis

unread,
Aug 28, 2015, 3:31:55 AM8/28/15
to Gerald Rosenberg, golang-nuts
On Fri, Aug 28, 2015 at 3:50 PM, <gerald.r...@gmail.com> wrote:
> Yes, but that really isn't at the crux of the problem or the fix.
>
> The strange looking thing is that by adding an '&' in the var assignment,
> sync.Locker in the method set Y suddenly accepts the structure X as
> implementing Y independent of whether the embedded field sync.Mutex is
> direct or pointer.
>
> Requiring an embedded pointer field to satisfy the pointer-receiver
> requirement of a method set member seems logically correlated. The
> obliteration of that correlation by a distant var assignment (spooky action
> at a distance) is, well, strange. Guessing it has something to do with the
> heuristics of the type-inferencing system, but a guess is far shy of a
> comfortable understanding.

There is no spooky at a distance action. The decision about whether a
type implements an interface is based on the type's method set.
http://golang.org/ref/spec#Method_sets

People often get confused about Method sets because of how the dot
selector will automatically take the address of receiver, so they
assume that T and *T both have the same method set.
http://golang.org/ref/spec#Selectors

The difference matter with interfaces because a value isn't
addressable while it's in an interface.
So if you want to call a method with a pointer receiver on a value in
an interface then the value must a pointer because the dot selector
can't just get you the address of that value.

Tim K

unread,
Aug 28, 2015, 12:09:26 PM8/28/15
to golang-nuts, gerald.r...@gmail.com, jes...@jessta.id.au
In addition, I recommend reading about how interfaces are implemented internally, this helped me understand why things work the way they do and it's easier to make a logical connection once you realize how it's implemented:

But I have to say that I still don't have a good logical grasp on what's addressable and what's not and why, I just have to remember it for now or let the compiler complain, think about it again and then fix it.

This is a good read too, the Map and Interfaces sections in particular (but start at the top, don't just jump there):

Gbr

unread,
Aug 28, 2015, 4:11:23 PM8/28/15
to golang-nuts, gerald.r...@gmail.com, jes...@jessta.id.au
Ok, aha moment I hope ...


On Friday, August 28, 2015 at 12:31:55 AM UTC-7, Jesse McNelis wrote:
There is no spooky at a distance action. The decision about whether a
type implements an interface is based on the type's method set.
http://golang.org/ref/spec#Method_sets

That has been my understanding.  But, it is not quite complete, though in hindsight the missing bit does seem obvious. 

That "decision of whether X implements Y" is evaluated only at the point of assignment.  That makes it sound like a run-time evaluation, but Go apparently evaluates it at compile-time and at every assignment that depends on whether X implements Y.  Different assignments -- pointer or value -- can result in different evaluations of whether X implements Y.  Absent an assignment, there is simply no relationship between X and Y -- Go has no 'extends' or 'implements'.

So, no spooky action at a distance - the variable assignment is the action.

James Aguilar

unread,
Aug 28, 2015, 6:54:52 PM8/28/15
to golang-nuts, gerald.r...@gmail.com, jes...@jessta.id.au
Not really. Let's try again from the top:

You can make a Y out of a *X, but not an X. This is because X does not implement Lock, since (*sync.Mutex).Lock() has a pointer receiver.

If you assign X{} to a variable, that variable's type is X. This variable will not be assignable to a Y, since, as discussed, it does not implement Lock(). If you assign &X{} to a variable, its type is *X.

Maybe it would help you understand if you wrote out the types? http://play.golang.org/p/RJcw2jSPPF





-- James

xingtao zhao

unread,
Aug 28, 2015, 7:53:49 PM8/28/15
to golang-nuts, gerald.r...@gmail.com, jes...@jessta.id.au
Should we think about a feature request for this? I mean: if X is anonymously embedded in Y, then Y will have the method set of X, and *Y will have the method set of *X?

Ian Lance Taylor

unread,
Aug 28, 2015, 8:02:01 PM8/28/15
to xingtao zhao, golang-nuts, gerald.r...@gmail.com, Jesse McNelis
On Fri, Aug 28, 2015 at 4:53 PM, xingtao zhao <zhaox...@gmail.com> wrote:
> Should we think about a feature request for this? I mean: if X is
> anonymously embedded in Y, then Y will have the method set of X, and *Y will
> have the method set of *X?

That is true today.

Ian

xingtao zhao

unread,
Aug 28, 2015, 8:22:15 PM8/28/15
to Ian Lance Taylor, golang-nuts, gerald.r...@gmail.com, Jesse McNelis
Oh, really?? That's great! I could simplify some part of my code! But I did not learn it from the language spec and Effective Go. Is it documented somewhere?

Thanks!

Ian Lance Taylor

unread,
Aug 28, 2015, 8:24:10 PM8/28/15
to xingtao zhao, golang-nuts, gerald.r...@gmail.com, Jesse McNelis
On Fri, Aug 28, 2015 at 5:22 PM, xingtao zhao <zhaox...@gmail.com> wrote:
> Oh, really?? That's great! I could simplify some part of my code! But I did
> not learn it from the language spec and Effective Go. Is it documented
> somewhere?

Look at the notes on promoted methods in
http://golang.org/ref/spec#Struct_types .

Ian

Gbr

unread,
Aug 28, 2015, 9:56:30 PM8/28/15
to golang-nuts, gerald.r...@gmail.com, jes...@jessta.id.au

On Friday, August 28, 2015 at 3:54:52 PM UTC-7, James Aguilar wrote:
Not really. Let's try again from the top:


All of what you said is true, though not directed to the crux of the problem I miserably tried to state in my OP which thankfully that Tim K correctly deduced and pointed out the correction for.  What Jesse McNelis pointed out is key: whether X implements Y is not solely determined by the functions on X and the method set of Y. Rather, whether X implements Y is evaluated dependent on the pointer/value type of each instance assignment.  It is a small nit, maybe even an odd way of looking at the problem, but that is what what I was missing.

Nonetheless, I appreciate your time in answering and apologize for the misleading question.

Thanks to all.

Reply all
Reply to author
Forward
0 new messages