[go-nuts] cannot use a type parameter as RHS in type declaration
Hi everyone! I wanted to ask about the error in the title: "cannot use a type parameter as RHS in type declaration". My use case: I am implementing range queries on a single dimension, and use a common type, e.g. int64. There is a method that I have to implement that is Contains(v int64) bool. For space efficiency, if for any reason a range of int64 numbers contains a single value, then I make let's say two implementations, one for 32 and one for 64 bytes. If the only value in the range can be represented with an int32, then I use it. Please, bear in mind that this is a simplification of my use case, but it correctly captures my concern. My attempt: type oneValue[T interface{int32 | int64}] T func (x oneValue[T]) Contains(v int64) bool { return v == int64(x) } As that causes the aformentioned error, my current approach is: type oneValue[T interface{int32 | int64}] [1]T func (x oneValue[T]) Contains(v int64) bool { return v == int64(x[0]) } Of course, this can also be expressed as: type oneValue[T interface{int32 | int64}] struct { Value T } func (x oneValue[T]) Contains(v int64) bool { return v == int64(x.Value) } I found that the two approaches are functionally equivalent for the most part (maybe I'm missing something). Some key points: 1. There doesn't seem to be any struct alignment, space use, or performance difference in neither of the three. 2. For generic versions, in general, a custom (un)marshaler may be needed (a dumb one pointing the (un)marshaler is enough). For JSON encoding, for example, the first generic version creates a JSON Array with a single element; and the second a JSON Object with a single `Value` member. Considering the three approaches: (A) non-generic; (B) generic with struct; (C) generic with an array of one element: 1. Can you explain why this error happens ? what problems it would create that was allowed? Any oneliner pointing to docs or just "look for x" is great! I may be missing something too obvious. 2. Are there other atlternatives you can think of, even dumb ones, and how would they compare to the three above? 3. Are there other differences or concerns you can think of, that were not addressed above? Thank you! -- 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/c34e29b7-242b-41f1-8ca4-4cca574fec7bn%40googlegroups.com.
[go-nuts] Add string Parser to x/tools/stringer
Very often I use the stringer command to generate lists of enumerated values, which is great btw. But one thing that I see myself also writing after this is a Parse method or function to validate strings that are received. The use case is that I have a list of possible values for a controlled field in a RESTful API. I receive and deliver string values from the API by I have a tight control inside the API itself. I declare a new type (generally an uint8 alias) and then declare a bunch of auto incremental constants, starting with the zero value that I always set to "Invalid" which is used as a control value. The stringer command makes the dull work but immediately after I write a Parse function or method. The following is a full example: //go:generate stringer -linecomment -output=enum_string.go -type=MyEnum package enum type MyEnum uint8 const ( // Reserved for internal use MyEnumInvalid MyEnum = iota // Invalid MyEnumValue1 // Value #1 MyEnumValue2 // Value #2 MyEnumValue3 // Value #3 MyEnumValue4 // Value #4 MyEnumValue5 // Value #5 ) func (e *MyEnum) Parse(value string) MyEnum { for lenIdx, lenVal, i := len(_MyEnum_index), uint8(len(value)), 1; i < lenIdx; i++ { if _MyEnum_index[i]-_MyEnum_index[i-1] == lenVal && // Maybe too small optimization to be perceptible/useful(?) _MyEnum_name[_MyEnum_index[i-1]:_MyEnum_index[i]] == value { *e = MyEnum(i - 1) return *e } } return MyEnum(0) } So this couples a lot to my workflow, of course. This way I can do something like: receivedVal = new(MyEnum).Parse(receivedStr) Or: var val *MyEnum for _, strVal := range strValues { if val.Parse(strVal) == MyEnumInvalid { return fmt.Errorf("Invalid value for enum: %v", strVal) } } So I started to write this function within the stringer command, which I modified to do so. So I don't know if this is for some use to the community. I know that in order for it to be useful to everyone it should be more general. So I'm looking at alternatives and, of course, if it's worth working on that. I it would be of some use there are two alternatives where this could live: - As an option to the stringer command - As a separate command (because it would exceed the stringer interface and would be misleading, since "Parse" is not part of the "Stringer" interface) And I'm only using here the stringer command to create series of consecutive numbers, so I would need to write extra code to fulfill the sparsiness of values (cases where stringer creates up to 10 different slices and after that just creates a map). It wouldn't be much work, actually, just read the code in the command. And instead of writing a new method for the type (breaking the idea of the "stringer" interface), I thought it could just be a "ParseT" function. Thoughts? Comments? Should I keep it for myself (hahaha)? -- 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/8b1d284c-9874-4e36-8883-a61c8eb218e7o%40googlegroups.com.
[go-nuts] [generics] Typo in draft
Hi, I think I spotted a small typo in the draft. In Examples > Containers we have: // Find returns the value associated with a key, or zero if not present. // The bool result reports whether the key was found. func (m *Map(K, V)) Find(k K) (V, bool) { pn := m.find(k) if *pn == nil { var zero val // see the discussion of zero values, above return zero, false } return (*pn).v, true } Whereas I think it should be: // Find returns the value associated with a key, or zero if not present. // The bool result reports whether the key was found. func (m *Map(K, V)) Find(k K) (V, bool) { pn := m.find(k) if *pn == nil { var zero V // see the discussion of zero values, above return zero, false } return (*pn).v, true } Patch attached. -- 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/CAGOxLdF76jLN0WCtPHP7FL1chVTJ5WJkf9%2BAmhVPgsyhh4W9OA%40mail.gmail.com. fix-typo.patch Description: Binary data
[go-nuts] Upgradable RLock
>From times to times I write a scraper or some other tool that would authenticate to a service and then use the auth result to do stuff concurrently. But when auth expires, I need to synchronize all my goroutines and have a single one do the re-auth process, check the status, etc. and then arrange for all goroutines to go back to work using the new auth result. To generalize the problem: multiple goroutines read a cached value that expires at some point. When it does, they all should block and some I/O operation has to be performed by a single goroutine to renew the cached value, then unblock all other goroutines and have them use the new value. I solved this in the past in a number of ways: having a single goroutine that handles the cache by asking it for the value through a channel, using sync.Cond (which btw every time I decide to use I need to carefully re-read its docs and do lots of tests because I never get it right at first). But what I came to do lately is to implement an upgradable lock and have every goroutine do: ** func (x implem) getProtectedValue() (someType, error) { // acquires a read lock that can be upgraded lock := x.upgradableLock.UpgradableRLock() // the Unlock method of the returned lock does the right thing // even if we later upgrade the lock defer lock.Unlock() // here we have read access to x.protectedValue // if the cached value is no longer valid, upgrade the lock // and update it if !isValid(x.protectedValue) && lock.TryUpgrade() { // within this if, we know we *do* have write access // to x.protectedValue x.protectedValue, x.protectedValueError = newProtectedValue() } // here we can only say everyone has read access to x.protectedValue // (the goroutine that got the lock upgrade could still write // here but as this code is shared, we should check the result // of the previous lock.TryUpgrade() again) return x.protectedValue, x.protectedValueError } ** The implementation is quite simple: ** // Upgradable implements all methods of sync.RWMutex, plus a new // method to acquire a read lock that can optionally be upgraded // to a write lock. type Upgradable struct { readers sync.RWMutex writers sync.RWMutex } func (m *Upgradable) RLock() { m.readers.RLock() } func (m *Upgradable) TryRLock() bool { return m.readers.TryRLock() } func (m *Upgradable) RUnlock() { m.readers.RUnlock() } func (m *Upgradable) RLocker() sync.Locker { return m.readers.RLocker() } func (m *Upgradable) Lock() { m.writers.Lock() m.readers.Lock() } func (m *Upgradable) TryLock() bool { if m.writers.TryLock() { if m.readers.TryLock() { return true } m.writers.Unlock() } return false } func (m *Upgradable) Unlock() { m.readers.Unlock() m.writers.Unlock() } // UpgradableRLock returns a read lock that can optionally be // upgraded to a write lock. func (m *Upgradable) UpgradableRLock() *UpgradableRLock { m.readers.RLock() return &UpgradableRLock{ m: m, unlockFunc: m.RUnlock, } } // UpgradableRLock is a read lock that can be upgraded to a write // lock. This is acquired by calling (*Upgradable). // UpgradableRLock(). type UpgradableRLock struct { mu sync.Mutex m *Upgradable unlockFunc func() } // TryUpgrade will attempt to upgrade the acquired read lock to // a write lock, and return whether it succeeded. If it didn't // succeed then it will block until the goroutine that succeeded // calls Unlock(). After unblocking, the read lock will still be // valid until calling Unblock(). // // TryUpgrade panics if called more than once or if called after // Unlock. func (u *UpgradableRLock) TryUpgrade() (ok bool) { u.mu.Lock() defer u.mu.Unlock() if u.m == nil { panic("TryUpgrade can only be called once and not after Unlock") } if ok = u.m.writers.TryLock(); ok { u.m.readers.RUnlock() u.m.readers.Lock() u.unlockFunc = u.m.Unlock } else { u.m.readers.RUnlock() u.m.writers.RLock() u.unlockFunc = u.m.writers.RUnlock } u.m = nil return } // Unlock releases the lock, whether it was a read lock or a write // lock acquired by calling Upgrade. // // Unlock panics if called more than once. func (u *UpgradableRLock) Unlock() { u.mu.Lock() defer u.mu.Unlock() if u.unlockFunc == nil { panic("Unlock can only be called once") } u.unlockFunc() u.unlockFunc = nil u.m = nil } ** I obviously try to avoid using it for other than protecting values that require potentially long I/O operations (like in my case re-authenticating to a web service) and also having a lot of interested goroutines. The good thing is that most of the time this pattern only requires one RLock, but the (*UpgradableRLock).Unlock requires an additional lock/unlock to prevent misusage of the upgradable lock (I could potentially get rid of it but preferred to keep
[go-nuts] Safely implementing Is(target error) bool
Hello everyone. In relation to standard errors package, I wanted to ask about safe ways of implementing Is(target error) bool. The current documentation (https://cs.opensource.google/go/go/+/refs/tags/go1.20.6:src/errors/wrap.go;l=35) says the following: // An error type might provide an Is method so it can be treated as equivalent // to an existing error. For example, if MyError defines // // func (m MyError) Is(target error) bool { return target == fs.ErrExist } This seems wrong and actually leads users in the wrong direction, as if someone implements an error type like "type MyErrs []error" then it's sufficient to make that implementation panic, so the documentation is leading users to create panic-prone code. While it's true that interfaces are comparable, concrete types implementing them may not, and the error will happen at runtime. Note that the code for the Is function in errors package actually uses reflectlite.TypeOf(target).Comparable() to avoid these panics, but that as users do not have access to reflectlite and, even if they do, the documentation is not warning against these possible panics, then it's very easy to make mistakes. Of course, nil comparisons are ok, because the comparison is of the value of the interface, not the value of the concrete type. I propose: consider changing the documentation to warn users against this possible problem when implementing the Is method of their own error types. My question to the community: how do you currently go about this issue. Thank you. -- 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/1fce8a3e-bcd2-41fc-b2e1-02fd70450c09n%40googlegroups.com.
[go-nuts] Re: Safely implementing Is(target error) bool
I apologize, I think the actual issue is with the implementation I found. Let me explain what happened to clarify: var sentinelErr = someInternalLib.NewError("something happened. The implementation of this contains a slice") type SomeInnocentErr struct {} func (e SomeInnocentErr) Error() string { return "wrapped: " + e.Err.Error() } func (e SomeInnocentErr) Is(target error) bool { return sentinelErr == target } If the above sentinelErr had been defined with the standard errors.New then there had been no problem, or at least instead of doing "return sentinelErr == target" it had done something like "errors.Is(target, sentinelErr)". This is evidently an internal issue, not with the standard library, sorry about the fuzz. On Monday, 31 July 2023 at 17:22:11 UTC-3 Diego Augusto Molina wrote: > Hello everyone. In relation to standard errors package, I wanted to ask > about safe ways of implementing Is(target error) bool. The current > documentation ( > https://cs.opensource.google/go/go/+/refs/tags/go1.20.6:src/errors/wrap.go;l=35) > > says the following: > > // An error type might provide an Is method so it can be treated as > equivalent > // to an existing error. For example, if MyError defines > // > // func (m MyError) Is(target error) bool { return target == fs.ErrExist } > > This seems wrong and actually leads users in the wrong direction, as if > someone implements an error type like "type MyErrs []error" then it's > sufficient to make that implementation panic, so the documentation is > leading users to create panic-prone code. While it's true that interfaces > are comparable, concrete types implementing them may not, and the error > will happen at runtime. > > Note that the code for the Is function in errors package actually uses > reflectlite.TypeOf(target).Comparable() > to avoid these panics, but that as users do not have access to reflectlite > and, even if they do, the documentation is not warning against these > possible panics, then it's very easy to make mistakes. Of course, nil > comparisons are ok, because the comparison is of the value of the > interface, not the value of the concrete type. > > I propose: consider changing the documentation to warn users against this > possible problem when implementing the Is method of their own error types. > > My question to the community: how do you currently go about this issue. > > Thank you. > -- 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/82a6f529-e6c1-4c45-b49e-363691366466n%40googlegroups.com.
[go-nuts] Generic zero value for compiler optimization
Hi, thank you for reading. Whenever I need to use a zero value for a generic type in a func I do something like the following: func SetZero[T any](pt *T) T { var zeroT T *pt = zeroT } That's all good, but I wonder how, if possible, it could be proved to the compiler that zeroT is the zero value for T. That would be to enable memclr optimization when bulk setting slice or array values to the zero value of their element type. Currently, as of 1.21, this only works when the src is a constant holding the zero value of the type. I also tried with something like *new(T), and it doesn't work either. But proving that the expression *new(T) in the src is the zero value for the type could potentially be easier than checking back if a certain variable (e.g. zeroT in the example) hasn't yet been reassigned or initialized to a non-zero value. Also, as there's no guarantee of what would T could hold, it could use memclrHasPointers if that makes sense, which seems like a fare tradeoff at least for now if we want to play with slices with generic element type. For reference, this is the code I'm trying: package main // file generic_slice_element_type_memclr.go func clear[T any](s []T) { for i := range s { s[i] = *new(T) } } func main() { clear([]int{1, 2, 3}) } And I'm compiling it with: $ go version go version go1.21.0 darwin/amd64 $ go tool compile -S generic_slice_element_type_memclr.go ... Kind regards. -- 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/b8ec1335-911c-42ed-96ce-a4b50153b8c9n%40googlegroups.com.
Re: [go-nuts] Generic zero value for compiler optimization
Thank you very much, that's actually what I was looking for. On Monday, 14 August 2023 at 13:57:35 UTC-3 Axel Wagner wrote: > You might be interested in https://github.com/golang/go/issues/61372 > > On Mon, Aug 14, 2023 at 3:52 PM Diego Augusto Molina < > diegoaugu...@gmail.com> wrote: > >> Hi, thank you for reading. Whenever I need to use a zero value for a >> generic type in a func I do something like the following: >> >> >> func SetZero[T any](pt *T) T { >> var zeroT T >> *pt = zeroT >> } >> >> >> That's all good, but I wonder how, if possible, it could be proved to the >> compiler that zeroT is the zero value for T. That would be to enable >> memclr optimization when bulk setting slice or array values to the zero >> value of their element type. Currently, as of 1.21, this only works when >> the src is a constant holding the zero value of the type. I also tried with >> something like *new(T), and it doesn't work either. But proving that the >> expression *new(T) in the src is the zero value for the type could >> potentially be easier than checking back if a certain variable (e.g. >> zeroT in the example) hasn't yet been reassigned or initialized to a >> non-zero value. >> Also, as there's no guarantee of what would T could hold, it could use >> memclrHasPointers if that makes sense, which seems like a fare tradeoff >> at least for now if we want to play with slices with generic element type. >> For reference, this is the code I'm trying: >> >> >> package main >> // file generic_slice_element_type_memclr.go >> >> func clear[T any](s []T) { >> for i := range s { >> s[i] = *new(T) >> } >> } >> >> func main() { >> clear([]int{1, 2, 3}) >> } >> >> >> And I'm compiling it with: >> >> >> $ go version >> go version go1.21.0 darwin/amd64 >> $ go tool compile -S generic_slice_element_type_memclr.go >> ... >> >> >> Kind regards. >> >> -- >> 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/b8ec1335-911c-42ed-96ce-a4b50153b8c9n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/golang-nuts/b8ec1335-911c-42ed-96ce-a4b50153b8c9n%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/65671596-e4ba-488a-946c-d00f9aef3f20n%40googlegroups.com.
[go-nuts] slog.GroupValue could potentially not make a new slice
Hello everyone, thank you for reading. I'm looking at the code of slog.GroupValue (https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/log/slog/value.go;l=171) and was wondering if we could benefit from reusing the same slice like this: // GroupValue returns a new Value for a list of Attrs. // The caller must not subsequently mutate the argument slice. func GroupValue(as ...Attr) Value { // Remove empty groups. // It is simpler overall to do this at construction than // to check each Group recursively for emptiness. var write int for read := range as { if as[read].isEmptyGroup() { as[read] = Attr{} // no need to keep a reference to the string Key } else { if read != write { as[write] = as[read] } write++ } } as = as[:write] return Value{num: uint64(len(as)), any: groupptr(unsage.SliceData(as))} } This, considering that the documentation of the func already states that the user must not mutate the argument slice, so not only the elements but the whole slice could be reused. In the case of having lots of attributes with empty group elements, then the stored slice will be unnecessarily larger by that number of Attr elements, but I wonder if that would be negligible, probably an edge case of misuse. Kind regards. -- 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/791c5981-95ea-44e4-a6f9-38cd65842ae4n%40googlegroups.com.
[go-nuts] Re: slog.GroupValue could potentially not make a new slice
Sorry, just thought it could also use clear to start getting accostumed :) // GroupValue returns a new Value for a list of Attrs. // The caller must not subsequently mutate the argument slice. func GroupValue(as ...Attr) Value { // Remove empty groups. // It is simpler overall to do this at construction than // to check each Group recursively for emptiness. var write int for read := range as { if !as[read].isEmptyGroup() { if read != write { as[write] = as[read] } write++ } } clear(as[write:]) // no need to keep a reference to the string Key as = as[:write] return Value{num: uint64(len(as)), any: groupptr(unsage.SliceData(as))} } On Monday, 14 August 2023 at 22:06:57 UTC-3 Diego Augusto Molina wrote: > Hello everyone, thank you for reading. I'm looking at the code of > slog.GroupValue ( > https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/log/slog/value.go;l=171) > > and was wondering if we could benefit from reusing the same slice like this: > > > // GroupValue returns a new Value for a list of Attrs. > // The caller must not subsequently mutate the argument slice. > func GroupValue(as ...Attr) Value { > // Remove empty groups. > // It is simpler overall to do this at construction than > // to check each Group recursively for emptiness. > var write int > for read := range as { > if as[read].isEmptyGroup() { > as[read] = Attr{} // no need to keep a reference to the > string Key > } else { > if read != write { > as[write] = as[read] > } > write++ > } > } > as = as[:write] > return Value{num: uint64(len(as)), any: groupptr(unsage.SliceData(as))} > } > > > This, considering that the documentation of the func already states that > the user must not mutate the argument slice, so not only the elements but > the whole slice could be reused. > In the case of having lots of attributes with empty group elements, then > the stored slice will be unnecessarily larger by that number of Attr > elements, but I wonder if that would be negligible, probably an edge case > of misuse. > > Kind regards. > -- 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/71b83821-b6e2-4ed0-9d36-a97ed1dba290n%40googlegroups.com.