(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.