(whoops: "I don't run into cases […] very often")

On Thu, Aug 27, 2020 at 2:12 PM Axel Wagner <axel.wagner...@googlemail.com>
wrote:

> On Thu, Aug 27, 2020 at 12:53 PM targe...@gmail.com <target....@gmail.com>
> wrote:
>
>> Because you then must remember about this specific case *every time *you
>> cast potentially nil pointer variable to interface. Every time you must
>> write `if x != nil { return x; } else { return nil; }` instead of just
>> `return x;`
>>
>
> This is true, but that alone is not indicative of a problem. You have to
> remember that `select` does a pseudo-random selection of cases everytime
> you use it, that you can't write to nil-maps, that dereferencing a
> nil-pointer might panic, that an index-expression might panic, that
> integer-addition might overflow, that floating-point math has
> counter-intuitive edge-cases, that init-functions run before main… you have
> to remember a *lot* of things every time you need them.
>
> I don't want to sound dismissive (as I said, I do think there is obviously
> *some* problem) but I don't run into cases where I would even be *tempted*
> to do this. Like, in your example, you'd need to a) have a declared
> pointer, b) would need to be oblivious to the fact on whether or not it's
> nil and c) it would have to be an invalid value if so and d) it would have
> to be a problem not caught by other means (e.g. returning a non-nil error
> alongside it). And when I am tempted to do this, I just know that the
> interface value will still be non-nil. And even when I don't, the most
> trivial of testing catches the problem.
>
> Like, I'm not saying it *never* happens or even that it never happens *to
> me*. But it seems very rarely cause significant problems and I don't
> understand why people put it so high up their list of being confusing or
> hard to remember.
>
>
>
>> > But solutions that try to give special treatment to nil-values when
>> they are put into interfaces just seem wrong to me.
>>
>> Because nils or null pointers are indeed special. They mean "Points to no
>> data". So dereference operation on such pointer leads to exceptional state.
>> In fact, nullable pointer in terms of richer type systems is a sum-type
>> `nil|*T` where `*T` always points to some valid object
>>
>
> Okay. But even in those languages, `nil` (or `None` or `Nothing` or
> whatever you call it is still a perfectly acceptable value, with
> significant semantic meaning. Yes, in those languages the compiler is able
> to prevent you from dereferencing it, but the value is still just a value
> like any other. If anything, the fact that even languages with very rich
> type-systems include types like `Maybe` shows how important `nil` as a
> possible value is. Otherwise you wouldn't have to put it in artificially.
>
> > The rule is very simple: A nil-interface is one that has no dynamic
>> value. All values are treated the same for this purpose. All types are
>> treated the same. I don't understand how that is anything but simple and
>> consistent. It might be less understandable for some other reason, but I
>> don't think it's simplicity or consistency.
>>
>> Please don't forget that interface pointer isn't simple. It's a so-called
>> "fat pointer" which consists of pointer to data and pointer to methods
>> table. Thus, we have 4 potential states
>>
>
> That's one way to look at it. And you are trying to make that way look
> pretty complicated. But you are talking about implementation details. It's
> like arguing strings are very complicated, because they can a) be
> string-constants held in read-only memory, b) be heap-allocated or c) be
> stack-allocated.
>
> Another way to look at it is that an interface is a type, that may or may
> not contain a dynamic value of some other type and allows calling methods
> on it. If it doesn't, the interface-value is nil. If it does, it isn't.
> That's the way interfaces are defined in the spec.
>
> I am still against teaching interfaces to newcomers as "two pointers, one
> to a type and one to a value". It leads to exactly the kinds of
> confusion you are expressing here, because people think about the
> representation and want to operate on that, instead of thinking about the
> semantics. It is completely unnecessary and often counterproductive to
> think of interfaces that way.
>
>
>> Why is this an issue? Because most interface implementations require
>> valid data pointer. "Static" (i.e. not needing data) implementations can be
>> considered corner case.
>
>
> It's not about needing data or not needing data. It's about deriving
> meaning from the fact that there is none. As I said, this is clearly
> important and used regularly - otherwise languages wouldn't need to add a
> `Maybe` (or whatever) type to do it.
>
> More importantly, if a pointer-type can't act on nil-pointers, that's a
> problem *orthogonal* to whether you put it into an interface or not. If you
> return an invalid value, it doesn't become valid just because you call it a
> `*T` instead of an `io.Reader`. If being able to return a `nil`-pointer as
> a type for which that isn't a valid value is a problem, then that should be
> impossible - not wrapping that value in an interface.
>
> So if we're hypothetically designing language with such fat pointers
>>
>
> You are reversing the approach here. We should design a language to have
> certain semantics and then write an implementation for that, not the other
> way around. By turning this around, you are limiting the solution space you
> are looking at, namely:
>
>
>> One way […]. The other way […]
>>
>
> I gave you a third solution: Having a separate identifier for the zero
> value of interfaces.
>
>
>> On Thursday, August 27, 2020 at 1:14:17 PM UTC+3
>> axel.wa...@googlemail.com wrote:
>>
>>> On Thu, Aug 27, 2020 at 11:39 AM targe...@gmail.com <targe...@gmail.com>
>>> wrote:
>>>
>>>> > I'm saying the current situation is less confusing than what you
>>>> describe, yes.
>>>> > AIUI, with what you describe, if I have a variable `x` of type `*T`
>>>> and an interface variable `y`, then `y = x` and `y = (*T)(x)` have
>>>> different semantics. I think it is strange to have a conversion of `x` *to
>>>> its own type* have any sort of semantic implication. It should be a no-op.
>>>>
>>>> It may be expressed in some different way. To me, if `x == nil` and
>>>> then `y != nil` after `y = x` is much more confusing.
>>>>
>>>
>>> And obviously you are not alone. Even though I really don't understand
>>> why this isn't just one of those "you learn about it, you know about it,
>>> you never run into any problems again" type of things. It does seem to come
>>> up sufficiently often to be a problem. And there are solutions that I think
>>> are fine. For example, using a different identifier (say `none`) to denote
>>> the zero-value of interfaces would be fine by me.
>>>
>>> But solutions that try to give special treatment to nil-values when they
>>> are put into interfaces just seem wrong to me. They single out nil-values
>>> as somehow special or less valid than other values. They single out
>>> pointer/slice/map/chan types as somehow special over int/bool/string/…
>>> types. It just seems undeniable to me, that they make the language *less*
>>> consistent.
>>>
>>> If you ask my opinion, I would make interfaces compare to nil on just
>>>> data pointer. If one wanted interface which doesn't require data, he
>>>> could've easily created one with static stub variable. No additional
>>>> checks, no "semi-nil" fat pointers, everything simple and consistent.
>>>>
>>>
>>> The rule is very simple: A nil-interface is one that has no dynamic
>>> value. All values are treated the same for this purpose. All types are
>>> treated the same. I don't understand how that is anything but simple and
>>> consistent. It might be less understandable for some other reason, but I
>>> don't think it's simplicity or consistency.
>>>
>>>
>>>> On Thursday, August 27, 2020 at 12:20:59 PM UTC+3
>>>> axel.wa...@googlemail.com wrote:
>>>>
>>>>> On Thu, Aug 27, 2020 at 11:10 AM targe...@gmail.com <
>>>>> targe...@gmail.com> wrote:
>>>>>
>>>>>> it would definitely. Though price for consistency looks very much
>>>>>> acceptable.
>>>>>
>>>>>
>>>>> I don't think "consistency" is at all the right word here. If
>>>>> anything, things would get *less* consistent, not more.
>>>>>
>>>>> > Personally, I would also find it very confusing, if converting a T
>>>>>> to a T changed program behavior
>>>>>> Sorry, didn't get it. Are you saying that nil pointer -> nil
>>>>>> interface is more confusing?
>>>>>>
>>>>>
>>>>> I'm saying the current situation is less confusing than what you
>>>>> describe, yes.
>>>>>
>>>>> AIUI, with what you describe, if I have a variable `x` of type `*T`
>>>>> and an interface variable `y`, then `y = x` and `y = (*T)(x)` have
>>>>> different semantics. I think it is strange to have a conversion of `x` *to
>>>>> its own type* have any sort of semantic implication. It should be a no-op.
>>>>>
>>>>>
>>>>>> On Thursday, August 27, 2020 at 11:49:16 AM UTC+3
>>>>>> axel.wa...@googlemail.com wrote:
>>>>>>
>>>>>>> On Thu, Aug 27, 2020 at 10:06 AM targe...@gmail.com <
>>>>>>> targe...@gmail.com> wrote:
>>>>>>>
>>>>>>>> Not sure if it was mentioned here, but IMO the main issues isn't
>>>>>>>> nil data itself, but how easy it's created. It'd be much less of a 
>>>>>>>> surprise
>>>>>>>> if creating nil-data required explicit cast from nil struct pointer to
>>>>>>>> interface pointer and resulted in just nil interface pointer in case of
>>>>>>>> implicit cast. Though such change is almost certainly breaking one.
>>>>>>>>
>>>>>>>
>>>>>>> This would require to insert extra nil-checks when assigning a
>>>>>>> pointer-value to an interface, as the compiler can't know if a pointer 
>>>>>>> is
>>>>>>> nil or not. Personally, I would also find it very confusing, if 
>>>>>>> converting
>>>>>>> a T to a T changed program behavior (though arguably, there is one such
>>>>>>> case currently with `uintptr(uintptr(unsafe.Pointer))`. But usage of
>>>>>>> `unsafe` seems sufficiently advanced).
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> On Monday, August 24, 2020 at 7:08:17 AM UTC+3 alex.be...@gmail.com
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> Can we at least move with the
>>>>>>>>> https://github.com/golang/go/issues/22729 , please? Anything will
>>>>>>>>> help with the current mess.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Sunday, August 23, 2020 at 8:52:30 PM UTC-7, Ian Lance Taylor
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>> On Sun, Aug 23, 2020 at 1:16 PM Denis Cheremisov
>>>>>>>>>> <denis.c...@gmail.com> wrote:
>>>>>>>>>> >
>>>>>>>>>> > You may use something like this
>>>>>>>>>> >
>>>>>>>>>> >         value2 :=
>>>>>>>>>> *(*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(&value)) + 8))
>>>>>>>>>> >         if value2 == 0 {
>>>>>>>>>> >                 return true
>>>>>>>>>> >         }
>>>>>>>>>> >
>>>>>>>>>> > on AMD64, should work also for any 64 bit architecture (at
>>>>>>>>>> least I believe so). Remember though this is hacky and may stop 
>>>>>>>>>> working
>>>>>>>>>> once.
>>>>>>>>>>
>>>>>>>>>> You could do that, but please don't.
>>>>>>>>>>
>>>>>>>>>> Ian
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> > воскресенье, 23 августа 2020 г. в 22:58:51 UTC+3, Aviv Eyal:
>>>>>>>>>> >>
>>>>>>>>>> >> I was trying to show that the current behavior is confusing
>>>>>>>>>> and that fmt.Print() needing to resort to panic-and-recover is kinda 
>>>>>>>>>> code
>>>>>>>>>> smell, but I sorts-of convinced myself that the current behavior is 
>>>>>>>>>> right,
>>>>>>>>>> or at least consistent.
>>>>>>>>>> >>
>>>>>>>>>> >> In my code, I got bit because I sometimes use v *Type to
>>>>>>>>>> denote "I may or may not have a value here" (where Type is a 
>>>>>>>>>> value-type).
>>>>>>>>>> >> This is probably a bad practice on my behalf, because I break
>>>>>>>>>> the Liskov substitution principle: there is a value of `*Type` that 
>>>>>>>>>> is not
>>>>>>>>>> a valid value of `Type`, and I let this value slip by.
>>>>>>>>>> >>
>>>>>>>>>> >> In this case, `v Type` implements Stringer (i.e. valid callee
>>>>>>>>>> for `v.String()`, but `v *Type`, in the strictest sense, does not.
>>>>>>>>>> >> The only reason we can write:
>>>>>>>>>> >>
>>>>>>>>>> >>     func (Type) String() string {...}
>>>>>>>>>> >>     v *Type = &Type{...}
>>>>>>>>>> >>     _ = v.String()
>>>>>>>>>> >>
>>>>>>>>>> >> and have it compile, is syntactic sugar: `v` gets implicitly
>>>>>>>>>> de-referenced, and there's an implicit assumption that it's not nil.
>>>>>>>>>> >> And there's a matching syntactic sugar for converting `Type`
>>>>>>>>>> to a `*Type`.
>>>>>>>>>> >>
>>>>>>>>>> >> So, In the code:
>>>>>>>>>> >>
>>>>>>>>>> >>     func (Type) String() string {...}
>>>>>>>>>> >>
>>>>>>>>>> >>     v *Type = nil
>>>>>>>>>> >>     r interface{} = v
>>>>>>>>>> >>     _, ok = r.(Stringer)
>>>>>>>>>> >>
>>>>>>>>>> >> What I really want to ask is "Can I, at runtime, call
>>>>>>>>>> r.String()?", whereas the question Go answers is "Is any of `r`, 
>>>>>>>>>> `*r`, or
>>>>>>>>>> `&r` defines .String()?" - which matches the static semantics of
>>>>>>>>>> `r.String()`.
>>>>>>>>>> >>
>>>>>>>>>> >> So, while I should probably not use *Type as a replacement for
>>>>>>>>>> Optional<Type>, I think it might make sense to have some operator 
>>>>>>>>>> that can
>>>>>>>>>> determine, at run-time, if a call `r.String()` is valid (including a
>>>>>>>>>> nil-check).
>>>>>>>>>> >>
>>>>>>>>>> >>
>>>>>>>>>> >> -- Aviv
>>>>>>>>>> >>
>>>>>>>>>> >> On Saturday, April 11, 2020 at 4:48:28 PM UTC+3
>>>>>>>>>> ren...@ix.netcom.com wrote:
>>>>>>>>>> >>>
>>>>>>>>>> >>> I agree with the OP. The usefulness of nil interfaces is
>>>>>>>>>> pretty limited. Show me a useful case that cant easily be 
>>>>>>>>>> implemented with
>>>>>>>>>> non-nil interfaces.
>>>>>>>>>> >>>
>>>>>>>>>> >>> I would argue that allowing nil interfaces causes more subtle
>>>>>>>>>> latent bugs and makes it harder to reason about the correctness of 
>>>>>>>>>> code
>>>>>>>>>> when reviewing it.
>>>>>>>>>> >>>
>>>>>>>>>> >>> It just feels wrong. I realize I’m probably in the minority
>>>>>>>>>> here but the OP is not alone.
>>>>>>>>>> >>>
>>>>>>>>>> >>> On Apr 11, 2020, at 8:20 AM, 'Axel Wagner' via golang-nuts <
>>>>>>>>>> golan...@googlegroups.com> wrote:
>>>>>>>>>> >>>
>>>>>>>>>> >>> On Fri, Apr 10, 2020 at 7:17 PM <cpu...@gmail.com> wrote:
>>>>>>>>>> >>>>
>>>>>>>>>> >>>> I realize I'm reviving an age-old discussion here and
>>>>>>>>>> apologize for bringing up the undead. I happend to run into this 
>>>>>>>>>> when my
>>>>>>>>>> application panicked when some interfaces where initialized with nil 
>>>>>>>>>> mock
>>>>>>>>>> objects instead of being left uninitialized as in production mode.
>>>>>>>>>> >>>
>>>>>>>>>> >>>
>>>>>>>>>> >>> Let's imagine a world in which `foo == nil` also is true if
>>>>>>>>>> `foo` is an interface-value containing a nil-pointer. Let's say in 
>>>>>>>>>> this
>>>>>>>>>> world, someone sends a message to golang-nuts. They wrote a mock for 
>>>>>>>>>> the
>>>>>>>>>> same code. And since it's just a mock, they just returned static 
>>>>>>>>>> value from
>>>>>>>>>> its methods and didn't need to care if the pointer was nil or not. 
>>>>>>>>>> They are
>>>>>>>>>> confused, because the passed in this mock, but the code just assumed 
>>>>>>>>>> the
>>>>>>>>>> field was uninitialized and never called into their mock. What would 
>>>>>>>>>> you
>>>>>>>>>> tell them? Why is their confusion less valid?
>>>>>>>>>> >>>
>>>>>>>>>> >>>> This would be an example where a nil implementing fooer is
>>>>>>>>>> never caught:
>>>>>>>>>> >>>>
>>>>>>>>>> >>>> type fooer interface {
>>>>>>>>>> >>>>  foo()
>>>>>>>>>> >>>> }
>>>>>>>>>> >>>>
>>>>>>>>>> >>>> type other struct{}
>>>>>>>>>> >>>>
>>>>>>>>>> >>>> func (o *other) foo() {} // implement fooer
>>>>>>>>>> >>>>
>>>>>>>>>> >>>> func main() {
>>>>>>>>>> >>>>  var f fooer
>>>>>>>>>> >>>>
>>>>>>>>>> >>>>  var p *other // nil
>>>>>>>>>> >>>>  f = p // it is a fooer so I can assign it
>>>>>>>>>> >>>>
>>>>>>>>>> >>>>  if f == nil {
>>>>>>>>>> >>>>     // will not get here
>>>>>>>>>> >>>>  }
>>>>>>>>>> >>>> }
>>>>>>>>>> >>>>
>>>>>>>>>> >>>>
>>>>>>>>>> >>>> My confusion comes from the point that the nil interface is
>>>>>>>>>> apparently not "a nil-pointer with the correct method set" while 
>>>>>>>>>> *other is
>>>>>>>>>> even if nil.
>>>>>>>>>> >>>
>>>>>>>>>> >>>
>>>>>>>>>> >>> In the code you posted, even a nil *other is a perfectly fine
>>>>>>>>>> implementation of fooer. You can call `(*other)(nil).foo()` without 
>>>>>>>>>> any
>>>>>>>>>> problems.
>>>>>>>>>> >>> So, as you illustrated, calling methods on a nil-pointer can
>>>>>>>>>> be totally fine. A nil-interface, OTOH, doesn't have any methods to 
>>>>>>>>>> call,
>>>>>>>>>> as it doesn't contain a dynamic value. If you write 
>>>>>>>>>> `(*other)(nil).foo()`,
>>>>>>>>>> it is completely clear what code gets called - even if that code 
>>>>>>>>>> *might*
>>>>>>>>>> panic. If you write `fooer(nil).foo()`, what code should be called 
>>>>>>>>>> in your
>>>>>>>>>> opinion?
>>>>>>>>>> >>>
>>>>>>>>>> >>> I think it's easy to see that a nil-interface and a
>>>>>>>>>> nil-pointer stored in an interface are very different things. Even 
>>>>>>>>>> from
>>>>>>>>>> first principles, without deep knowledge of the language. And if 
>>>>>>>>>> they are
>>>>>>>>>> obviously different, I don't understand why you'd find it confusing 
>>>>>>>>>> that
>>>>>>>>>> they are not the same in this particular manner.
>>>>>>>>>> >>>
>>>>>>>>>> >>>> The above is a case where that might happen. In can be
>>>>>>>>>> worked around but it is unexpected unless the programmer is deeply 
>>>>>>>>>> rooted
>>>>>>>>>> in the language definition.
>>>>>>>>>> >>>
>>>>>>>>>> >>>
>>>>>>>>>> >>> I fully agree with that. What I *don't* agree with, is where
>>>>>>>>>> you attribute the problem here. You say, the problem is that the 
>>>>>>>>>> nil-check
>>>>>>>>>> is ill-behaved. I say that - if anything - the original 
>>>>>>>>>> nil-assignment is
>>>>>>>>>> ill-behaved. Having `(fooer)((*other)(nil)) == nil` be true is 
>>>>>>>>>> semantically
>>>>>>>>>> wrong, because by checking against `nil`, you are checking if you 
>>>>>>>>>> have a
>>>>>>>>>> correct implementation - and you might well have a correct 
>>>>>>>>>> implementation,
>>>>>>>>>> even if it's using a nil-pointer.
>>>>>>>>>> >>>
>>>>>>>>>> >>> Note, that the contained pointer being nil isn't the *only*
>>>>>>>>>> case in which calling the method might panic. For example, what 
>>>>>>>>>> about this
>>>>>>>>>> code?
>>>>>>>>>> >>> https://play.golang.org/p/lNq0qphez7v
>>>>>>>>>> >>> Shouldn't the `nil`-check also catch that? After all, calling
>>>>>>>>>> the method panics, so it's clearly not a valid implementation - even 
>>>>>>>>>> if x
>>>>>>>>>> itself is not nil. Why is a nil-pointer more special than any other 
>>>>>>>>>> value
>>>>>>>>>> that causes a method to panic?
>>>>>>>>>> >>>
>>>>>>>>>> >>>> Seems as of today that there is no tooling to support that
>>>>>>>>>> check. Maybe it's not a widespread issue.
>>>>>>>>>> >>>
>>>>>>>>>> >>>
>>>>>>>>>> >>> As of today, the language also isn't changed :) Maybe someone
>>>>>>>>>> who think this is important enough to change the language, could 
>>>>>>>>>> also feel
>>>>>>>>>> it's important enough to write this tooling.
>>>>>>>>>> >>>
>>>>>>>>>> >>>>
>>>>>>>>>> >>>> --
>>>>>>>>>> >>>> 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...@googlegroups.com.
>>>>>>>>>> >>>> To view this discussion on the web visit
>>>>>>>>>> https://groups.google.com/d/msgid/golang-nuts/e0dbcd38-510e-43b9-b363-2af1c636250b%40googlegroups.com.
>>>>>>>>>>
>>>>>>>>>> >>>
>>>>>>>>>> >>> --
>>>>>>>>>> >>> 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...@googlegroups.com.
>>>>>>>>>> >>>
>>>>>>>>>> >>> To view this discussion on the web visit
>>>>>>>>>> https://groups.google.com/d/msgid/golang-nuts/CAEkBMfEPjcsZ3enqXyt%2BUphFJ1cNQ81cFCcjfwwkQZKHMrjSzA%40mail.gmail.com.
>>>>>>>>>>
>>>>>>>>>> >
>>>>>>>>>> > --
>>>>>>>>>> > 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 golan...@googlegroups.com.
>>>>>>>>>>
>>>>>>>>> > To view this discussion on the web visit
>>>>>>>>>> https://groups.google.com/d/msgid/golang-nuts/c1ed2e38-6215-4ed2-8357-f8b5d83bf1a7n%40googlegroups.com.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>> --
>>>>>>>> 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...@googlegroups.com.
>>>>>>>>
>>>>>>> To view this discussion on the web visit
>>>>>>>> https://groups.google.com/d/msgid/golang-nuts/84244528-84e6-4c2e-89bf-7fbf0590e132n%40googlegroups.com
>>>>>>>> <https://groups.google.com/d/msgid/golang-nuts/84244528-84e6-4c2e-89bf-7fbf0590e132n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>> .
>>>>>>>>
>>>>>>> --
>>>>>> 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...@googlegroups.com.
>>>>>>
>>>>> To view this discussion on the web visit
>>>>>> https://groups.google.com/d/msgid/golang-nuts/46d92421-a3a8-4b8a-b557-aa14d79e55b6n%40googlegroups.com
>>>>>> <https://groups.google.com/d/msgid/golang-nuts/46d92421-a3a8-4b8a-b557-aa14d79e55b6n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>> .
>>>>>>
>>>>> --
>>>> 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...@googlegroups.com.
>>>>
>>> To view this discussion on the web visit
>>>> https://groups.google.com/d/msgid/golang-nuts/31df134b-7e55-4f32-9e1f-6d974817891en%40googlegroups.com
>>>> <https://groups.google.com/d/msgid/golang-nuts/31df134b-7e55-4f32-9e1f-6d974817891en%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>> .
>>>>
>>> --
>> 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.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/golang-nuts/4613df4c-9342-4285-9b8d-823afa82f58en%40googlegroups.com
>> <https://groups.google.com/d/msgid/golang-nuts/4613df4c-9342-4285-9b8d-823afa82f58en%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>

-- 
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.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAEkBMfEOntSMHLwy-yJxQogJX2hcXeoQjU0gAzsqQYfSONtadw%40mail.gmail.com.

Reply via email to