appreciated ;)
Looks cool man
Why are errors ignored in AwaitAll?
Tests are written completely wrong, use the built-in testing package
ioutil.ReadAll is deprecated, use io.ReadAll instead
So do you think returning a slice of errors is a correct approach for error handling? Or maybe is AwaitAll just an unnecessary utility?
This whole lib is actually unnecessary 🙂
Wait, What ? Can someone please confirm, Is returning a slice of errors, a bad approach?
Can't think of a normal case where you would return slice of errors. Even in a validator I'd have a struct like this: type ValidatorErrors struct { Username error Email error ... }
I was thinking of a password checker (new password strength requirements). i.e. A slice of errors with all conditions not met and to print out to the user. Any particular design reason to not use a slice of errors?
I understand the recommendation instead is as below, but just a little curious why not a slice? struct { Length Numeric Symbol Uppercase … }
Even in that case I prefer a struct with fields for each check. Logically when we're moving forward with a function, an error tells us to stop and we can't move deeper without fixing the errors. But there are some cases like validators, where you need to inform users about all the failures. In such cases I think it's more readable and go-style to define custom error structs. But I'm not sure if there are any design reasons.
Thanks! Exactly that but it is down to the implementation really. The validator function if returning a slice of errors, can be written to not just return a single error but a descriptive slice to all conditions not met. One i can think of is that it can be seen as breaking the common way we would handle an error i.e. err := func x() where with a slice you would have to len(err) > 0 instead of standard err != nil. Is that it?
Correct. Though, considering UX where you're showing the errors to the user, it's not a good practice to output all the errors with line breaks. If you have different fields, then you'll have to relate each error to its own field. In that case you should separate errors somehow. Like adding the field's name before the error: errors.New("username: invalid'). Which I think in this case returning a struct is more readable and easier to work with.
Compared to: errs := []error{ errors.New("username: too short" ), errors.New("password: not strong" ), }
Devsbot.GoFmt lol
I think we are on similar thought train but not close enough. A slice of errors is a “collection” of <T> error representing each condition not met. I am responding to your “line break” remark.
How would that be in code?
Just an overview: func validateNewPassword(pwd string) []error { errSl := []error{} if !hasLowerCase { errSl = append(errSl, errNoLowerCase) } if !hasUpperCase { errSl = append(errSl, errNoUpperCase) } if !hasNumber { errSl = append(errSl, errNoNumber) } if !hasSymbol { errSl = append(errSl, errNoSymbol) } ... return errSl }
Now that its clear to me, I do like a struct with each specific field representing an error than just a returning bunch of errors. So thank you, Point accepted! 😊
why would you want a slice of errors?
I try to avoid slices because they cause allocations password recommendations should really be done on the frontend. The backend should only return a debug message for the FE developers { "error": { "code": "INVALID_PASSWORD", "message": "Password contains illegal characters" } }
that is just aweful
What? So, No last check at the backend especially before writing into a DB? Edit: I see what you mean. You would do the checks but rather return a readable error message in json format. Agree!
Dont you think it would be an appropriate use case to use a slice of errors if you expect to do N independent actions in a bulk request. For e.g. func DeleteUsers(ids []UserID) []error It would be so much easier to process a 1:1 comparison b/w input and output.
there's a check, but not a whole bunch of error messages for the end-user. All end-user related information is to be provided by the frontend
no. a function like this looks like a transaction to me. Either all users are deleted or none. There's only one error.
if, however, you want it to not be a transaction then consider using lambda functions instead: func DeleteUsers(ids []UserID, onErr func(index int, err error)) error this way you avoid generating unnecessary garbage and reduce pressure on the GC.
So I have made another version, Avoiding reflection to make the code much faster. Though, the price is paid with an ugly boilerplate wrapper which doesn't break go as a language but does break my eyes What do you think? Is it completely unacceptable? https://github.com/Amirsil/Asynchronous-Go/tree/generics#asynchronous-go
I still see reflection there
Обсуждают сегодня