return nil }) // false ok := lo.Try(func() error { return nil }) // true ok := lo.Try(func() error { return errors.New("error") }) // false ``` [[play](https://go.dev/play/p/mTyyWUvn9u4)] ### Try{0->6} The same behavior as `Try`, but the callback returns 2 variables. ```go ok := lo.Try2(func() (string, error) { panic("error") return "", nil }) // false ``` [[play](https://go.dev/play/p/mTyyWUvn9u4)] ### TryOr Calls the function and return a default value in case of error and on panic. ```go str, ok := lo.TryOr(func() (string, error) { panic("error") return "hello", nil }, "world") // world // false str, ok := lo.TryOr(func() error { return "hello", nil }, "world") // hello // true str, ok := lo.TryOr(func() error { return "hello", errors.New("error") }, "world") // world // false ``` [[play](https://go.dev/play/p/B4F7Wg2Zh9X)] ### TryOr{0->6} The same behavior as `TryOr`, but the callback returns `X` variables. ```go str, nbr, ok := lo.TryOr2(func() (string, int, error) { panic("error") return "hello", 42, nil }, "world", 21) // world // 21 // false ``` [[play](https://go.dev/play/p/B4F7Wg2Zh9X)] ### TryWithErrorValue The same behavior as `Try`, but also returns the value passed to panic. ```go err, ok := lo.TryWithErrorValue(func() error { panic("error") return nil }) // "error", false ``` [[play](https://go.dev/play/p/Kc7afQIT2Fs)] ### TryCatch The same behavior as `Try`, but calls the catch function in case of error. ```go caught := false ok := lo.TryCatch(func() error { panic("error") return nil }, func() { caught = true }) // false // caught == true ``` [[play](https://go.dev/play/p/PnOON-EqBiU)] ### TryCatchWithErrorValue The same behavior as `TryWithErrorValue`, but calls the catch function in case of error. ```go caught := false ok := lo.TryCatchWithErrorValue(func() error { panic("error") return nil }, func(val any) { caught = val == "error" }) // false // caught == true ``` [[play](https://go.dev/play/p/8Pc9gwX_GZO)] ### ErrorsAs A shortcut for: ```go err := doSomething() var rateLimitErr *RateLimitError if ok := errors.As(err, &rateLimitErr); ok { // retry later } ``` single line `lo` helper: ```go err := doSomething() if rateLimitErr, ok := lo.ErrorsAs[*RateLimitError](err); ok { // retry later } ``` [[play](https://go.dev/play/p/8wk5rH8UfrE)] ### Assert Does nothing when the condition is `true`, otherwise it panics with an optional message. Think twice before using it, given that [Go intentionally omits assertions from its standard library](https://go.dev/doc/faq#assertions). ```go age := getUserAge() lo.Assert(age >= 15) ``` ```go age := getUserAge() lo.Assert(age >= 15, "user age must be >= 15") ``` [[play](https://go.dev/play/p/Xv8LLKBMNwI)] ### Assertf Like `Assert`, but with `fmt.Printf`-like formatting. Think twice before using it, given that [Go intentionally omits assertions from its standard library](https://go.dev/doc/faq#assertions). ```go age := getUserAge() lo.Assertf(age >= 15, "user age must be >= 15, got %d", age) ``` [[play](https://go.dev/play/p/TVPEmVcyrdY)] ## 🛩 Benchmark We executed a simple benchmark with a dead-simple `lo.Map` loop: See the full implementation [here](./map_benchmark_test.go). ```go _ = lo.Map[int64](arr, func(x int64, i int) string { return strconv.FormatInt(x, 10) }) ``` **Result:** Here is a comparison between `lo.Map`, `lop.Map`, `go-funk` library and a simple Go `for` loop. ```shell $ go test -benchmem -bench ./... goos: linux goarch: amd64 pkg: github.com/samber/lo cpu: Intel(R) Core(TM) i5-7267U CPU @ 3.10GHz cpu: Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz BenchmarkMap/lo.Map-8 8 132728237 ns/op 39998945 B/op 1000002 allocs/op BenchmarkMap/lop.Map-8 2 503947830 ns/op 119999956 B/op 3000007 allocs/op BenchmarkMap/reflect-8 2 826400560 ns/op 170326512 B/op 4000042 allocs/op BenchmarkMap/for-8 9 126252954 ns/op 39998674 B/op 1000001 allocs/op PASS ok github.com/samber/lo 6.657s ``` - `lo.Map` is way faster (x7) than `go-funk`, a reflection-based Map implementation. - `lo.Map` has the same allocation profile as `for`. - `lo.Map` is 4% slower than `for`. - `lop.Map` is slower than `lo.Map` because it implies more memory allocation and locks. `lop.Map` is useful for long-running callbacks, such as i/o bound processing. - `for` beats other implementations for memory and CPU. ## 🤝 Contributing - Ping me on Twitter [@samuelberthe](https://twitter.com/samuelberthe) (DMs, mentions, whatever :)) - Fork the [project](https://github.com/samber/lo) - Fix [open issues](https://github.com/samber/lo/issues) or request new features Don't hesitate ;) Helper naming: helpers must be self-explanatory and respect standards (other languages, libraries...). Feel free to suggest many names in your contributions. ```bash # Install some dev dependencies make tools # Run tests make test # or make watch-test ``` ## 👤 Contributors ![Contributors](https://contrib.rocks/image?repo=samber/lo) ## 💫 Show your support Give a ⭐️ if this project helped you! [![GitHub Sponsors](https://img.shields.io/github/sponsors/samber?style=for-the-badge)](https://github.com/sponsors/samber) ## 📝 License Copyright © 2022 [Samuel Berthe](https://github.com/samber). This project is under [MIT](./LICENSE) license.