{"id":87,"date":"2024-12-25T23:24:55","date_gmt":"2024-12-25T23:24:55","guid":{"rendered":"https:\/\/douglasgoulart.dev\/?p=87"},"modified":"2024-12-25T23:24:55","modified_gmt":"2024-12-25T23:24:55","slug":"iterating-smarter-unlocking-gos-new-range-with-functions","status":"publish","type":"post","link":"https:\/\/douglasgoulart.dev\/?p=87","title":{"rendered":"Iterating Smarter: Unlocking Go&#8217;s New range with Functions"},"content":{"rendered":"\n<p>Lately, I\u2019ve been exploring some of the newer features in Go, and one that really got me hooked is the ability to use <code>range<\/code> with functions, introduced in Go 1.23. It\u2019s a feature that, at first glance, might seem like a minor tweak, but once you dig in, it opens up exciting possibilities for writing cleaner, more modular code.<\/p>\n\n\n\n<p>This article is all about diving into this feature hands-on. My goal here isn\u2019t just to explain it but to get my hands dirty with practical examples\u2014and maybe inspire you to try it out too. Let\u2019s figure out together how push and pull iterators can simplify common patterns, make your code more maintainable, and maybe even spark some fresh ideas for your next project. So, grab your favorite editor, and let\u2019s learn something new!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">TL;DR<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Go 1.23 Feature Highlight:<\/strong> Introduced the ability to use <code>range<\/code> with functions, enabling concise and modular iteration logic.<\/li>\n\n\n\n<li><strong>Push Iterators:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Centralize iteration logic in the <code>All<\/code> method, simplifying repetitive traversal.<\/li>\n\n\n\n<li>Enable operations like <code>Filter<\/code>, <code>Search<\/code>, and <code>Map<\/code> with minimal code changes.<\/li>\n\n\n\n<li>Follow the <strong>Open\/Closed<\/strong> and <strong>DRY<\/strong> principles, making code extensible and maintainable.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Pull Iterators:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Provide on-demand data retrieval using the <code>PullIterator<\/code> method.<\/li>\n\n\n\n<li>Ideal for complex tasks like playlist comparison or lazy evaluations.<\/li>\n\n\n\n<li>Enhance performance and memory usage by processing elements only when required.<\/li>\n\n\n\n<li>Allow flexible pausing and resuming of iteration, supporting advanced workflows.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Adapters for Enhanced Composition:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Use functions like <code>Filter<\/code> and <code>Map<\/code> to chain operations on sequences.<\/li>\n\n\n\n<li>Promote reusability and clarity in workflows like filtering and transforming playlist data.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Performance Insights:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Direct map iteration is slightly faster than <code>maps.All<\/code>, but the latter offers cleaner composition for more complex operations.<\/li>\n\n\n\n<li>Trade-offs between readability and performance depend on your application\u2019s needs.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Key Takeaways:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Combining push and pull iterators with adapters results in highly modular, maintainable code.<\/li>\n\n\n\n<li>Iterators open possibilities for handling linked lists, maps, and other structures elegantly.<\/li>\n\n\n\n<li>Go&#8217;s new iterator features provide both functional programming power and practical use cases for developers.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">A Simple Push Iterator Use Case<\/h2>\n\n\n\n<p>Imagine that you&#8217;re creating an audio streaming service like Spotify or Deezer. You&#8217;re requested to develop a feature to display the total length of a playlist. This is the current linked list structure of your company:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"126\" src=\"https:\/\/douglasgoulart.dev\/wp-content\/uploads\/2024\/12\/image-1024x126.png\" alt=\"\" class=\"wp-image-89\" srcset=\"https:\/\/douglasgoulart.dev\/wp-content\/uploads\/2024\/12\/image-1024x126.png 1024w, https:\/\/douglasgoulart.dev\/wp-content\/uploads\/2024\/12\/image-300x37.png 300w, https:\/\/douglasgoulart.dev\/wp-content\/uploads\/2024\/12\/image-768x94.png 768w, https:\/\/douglasgoulart.dev\/wp-content\/uploads\/2024\/12\/image-1536x189.png 1536w, https:\/\/douglasgoulart.dev\/wp-content\/uploads\/2024\/12\/image.png 1594w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Each song has its own duration, but the current song from the playlist points to the next.<br>Translating this to go code means something like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">type Song struct {\n\tTitle    string\n\tArtist   string\n\tDuration time.Duration\n}\n\ntype PlaylistSong struct {\n\tSong *Song\n\tNext *PlaylistSong\n}\n\ntype Playlist struct {\n\tHead *PlaylistSong\n}<\/pre>\n\n\n\n<p>With this piece of code in your hands you can tell me: &#8220;Hey, that&#8217;s pretty straightforward. I just have to iterate over each item and get the duration.&#8221;.<br>As a result that&#8217;s the code that you produce:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func (p *Playlist) GetDuration() time.Duration {\n    var duration time.Duration\n    for currentSong := p.Head; currentSong != nil; currentSong = currentSong.Next {\n        duration += currentSong.Song.Duration\n    }\n    return duration\n}<\/pre>\n\n\n\n<p>The result of this code might be something like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Playlist Duration: 16m0s<\/pre>\n\n\n\n<p>The result is working as expected and all of your tests are passing flawlessly.<br>After a few minutes and a few cups of coffee, you receive a code review from your teammate saying:<br><code>Hey, I'm implementing a feature to filter songs from a playlist by the artist and song's name. I'm implementing an iterator pattern at PlaylistSong. Why don't you give it a try?<\/code><\/p>\n\n\n\n<p>Why can&#8217;t she implement it just like me? It would be something like:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func (p *Playlist) Search(term string) *PlaylistSong {\n    for currentSong := p.Head; currentSong != nil; currentSong = currentSong.Next {\n        \/\/ Do search for each element\n    }\n}<\/pre>\n\n\n\n<p>The main problem is this for loop. It is a little messy and you will have to repeat yourself every single time that you want to get your entire playlist. Maybe we shouldn&#8217;t have picked a linked list at the beginning to represent a playlist, but it is too late right now for refactoring and you and your teammate have a schedule to follow.<br>So, you decided to search for the suggested approach and find <a href=\"https:\/\/go.dev\/blog\/range-functions\">this article<\/a> from Ian Lance Taylor telling about this new go feature.<br>After reading you came up with this solution that fits perfectly for you, your teammate, and any future implementations that might need the full playlist:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func (p *Playlist) All() iter.Seq[PlaylistSong] {\n    return func(yield func(PlaylistSong) bool) {\n        for currentSong := p.Head; currentSong != nil; currentSong = currentSong.Next {\n            if !yield(*currentSong) {\n                return\n            }\n        }\n    }\n}\n\nfunc (p *Playlist) GetDuration() time.Duration {\n    var duration time.Duration\n    for currentSong := range p.All() {\n        duration += currentSong.Song.Duration\n    }\n    return duration\n}\n\nfunc (p *Playlist) Search(term string) *Playlist {\n    for currentSong := range p.All() {\n        \/\/ Do search for each element\n    }\n}<\/pre>\n\n\n\n<p>In Go 1.23, a new language feature allows the <code>range<\/code> keyword to iterate over functions that return elements sequentially. This enhancement enables more concise and readable code when traversing data structures like linked lists.<br>To leverage this feature, we define a <code>All<\/code> method for the <code>Playlist<\/code> struct. This method returns a function that yields each <code>PlaylistSong<\/code> in sequence.<br>Here, <code>iter.Seq[PlaylistSong]<\/code> represents a function type that accepts a <code>yield<\/code> function. This <code>yield<\/code> function processes each <code>PlaylistSong<\/code> and returns a boolean indicating whether to continue iteration. The <code>All<\/code> method traverses the linked list, invoking <code>yield<\/code> for each song until the end of the list or until <code>yield<\/code> returns <code>false<\/code>.<br>Using the <code>All<\/code> function, we are now able to <div> use our sequence without having to know about the linked list or any other implementation details.<\/div><\/p>\n\n\n\n<p>You are now able to build a function that you have seen in other languages like <code>Filter<\/code> . With the code below from Taylor&#8217;s article:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Filter returns a new sequence containing elements from 's' that satisfy the predicate 'f'.\nfunc Filter[V any](f func(V) bool, s iter.Seq[V]) iter.Seq[V] {\n    \/\/ Return an iterator function that takes a 'yield' function as its parameter.\n    return func(yield func(V) bool) {\n        \/\/ Iterate over each element 'v' in the input sequence 's'.\n        for v := range s {\n            \/\/ If the element satisfies the predicate 'f'...\n            if f(v) {\n                \/\/ ...pass it to 'yield'. If 'yield' returns false, stop iteration.\n                if !yield(v) {\n                    return\n                }\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<p>The only change is that now we have added a <code>f<\/code> function to check if the element should or not be considered at the final <code>Seq<\/code>.<br>And now we are able to implement a filter function into our <code>Playlist<\/code> like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func (p *Playlist) Filter(f func(PlaylistSong) bool) *Playlist {\n    result := Filter(f, p.All())\n    filteredPlaylist := Playlist{}\n    for currentSong := range result {\n        filteredPlaylist.AddSong(currentSong.Song)\n    }\n\n    return &amp;filteredPlaylist\n}<\/pre>\n\n\n\n<p>Now, we can simply implement a <code>Search<\/code> with:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func (p *Playlist) Search(term string) *Playlist {\n    return  p.Filter(func(song PlaylistSong) bool { return song.Song.Title == term || song.Song.Artist == term })\n}<\/pre>\n\n\n\n<p>This way we are allowing the extension of the playlist functionality without modification, adhering to the <strong>Open\/Closed Principle<\/strong> from SOLID. By implementing the <code>All<\/code> method and using it as a base for operations like <code>GetDuration<\/code>, <code>Search<\/code>, and <code>Filter<\/code>, we eliminate the repetitive traversal logic that would otherwise appear in each method. This makes the code more maintainable and less prone to errors when changes or new features are required.<\/p>\n\n\n\n<p>Additionally, this approach follows the <strong>DRY (Don&#8217;t Repeat Yourself)<\/strong> principle by centralizing the iteration logic in the <code>All<\/code> method. Repeated iteration code across different methods is replaced with reusable sequences, ensuring consistency and reducing redundancy. If the underlying data structure for the playlist changes in the future, we only need to update the <code>All<\/code> method, and all dependent methods will continue to work seamlessly.<\/p>\n\n\n\n<p>By combining these principles, the code becomes more modular, extensible, and easier to understand. New features, such as sorting or mapping songs to a new structure, can be added by leveraging the same iterator-based approach, further enhancing the system&#8217;s flexibility without sacrificing readability or maintainability.<\/p>\n\n\n\n<p>This <del>stupid<\/del> example shows us how to build a Push Iterator.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Implementing Pull Iterators in Go<\/h2>\n\n\n\n<p>Just as you&#8217;re celebrating the newfound elegance of the <code>All<\/code> method and the iterator magic, your product manager comes in with another feature request. This time, you&#8217;re asked to compare two playlists and calculate a similarity score. The goal is to measure how similar two playlists are based on their songs and the order in which they appear.<\/p>\n\n\n\n<p>With a push iterator, this would mean writing repetitive, messy traversal logic again. However, with pull iterators, we can make this task cleaner and more modular. Let\u2019s first set up the groundwork for implementing the comparison.<\/p>\n\n\n\n<p>To simplify traversal for comparison, we can use a pull iterator for the Playlist:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ PullIterator provides a pull-style iterator over the playlist songs.\nfunc (p *Playlist) PullIterator() (next func() (PlaylistSong, bool), stop func()) {\n    return iter.Pull(p.All())\n}<\/pre>\n\n\n\n<p>This <code>PullIterator<\/code> method converts our push-style iterator from the <code>All<\/code> method into a pull-style iterator, giving us <code>next<\/code> and <code>stop<\/code> functions. These functions allow us to fetch items on demand, making it easy to compare two playlists step-by-step.<\/p>\n\n\n\n<p>With the pull iterator in place, implementing a similarity function becomes straightforward. Here\u2019s how we can proceed:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func (p *Playlist) Similarity(other *Playlist) float64 {\n    nextA, stopA := p.PullIterator()\n    defer stopA()\n\n    nextB, stopB := other.PullIterator()\n    defer stopB()\n\n    var matches, total int\n    for {\n        songA, okA := nextA()\n        songB, okB := nextB()\n\n        if !okA || !okB {\n            break\n        }\n\n        total++\n\n        \/\/ Compare songs based on title and artist (ignoring duration for now)\n        if songA == songB {\n            matches++\n        }\n    }\n\n    \/\/ If both playlists have the same number of songs, calculate similarity based on matches.\n    if total == 0 {\n        return 0.0\n    }\n\n    return float64(matches) \/ float64(total)\n}<\/pre>\n\n\n\n<p>But you might be thinking: What are the advantages of using Pull Iterators?<\/p>\n\n\n\n<p>They enable on-demand data retrieval, fetching elements one at a time only when needed, which reduces memory usage and improves performance for large datasets. This approach is particularly useful in scenarios involving comparisons or synchronizations, as it simplifies traversal by allowing step-by-step iteration without preloading sequences. Additionally, pull iterators promote a clean separation of concerns by decoupling the logic for data generation from its consumption, making the code modular and easier to maintain. Their lazy evaluation ensures elements are processed only when required, optimizing resource usage and supporting operations like filtering or mapping incrementally. Lastly, pull iterators offer flexibility in pausing and resuming iteration, making them ideal for complex tasks or intermediate computations without the overhead of maintaining an external state.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A few more cool Iterator features<\/h2>\n\n\n\n<p>One of the most powerful aspects of Go&#8217;s new iterator capabilities is the use of <strong>adapters<\/strong>, which allow you to compose sequences seamlessly. Adapters let you chain operations like filtering, mapping, or sorting without modifying the underlying data structure. Imagine a music app where you want to filter songs by a specific artist, and then transform them to display only the title and duration. With adapters, this becomes straightforward and highly reusable.<\/p>\n\n\n\n<p>First, we will implement a new <code>Map<\/code> function:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func Map[V any, R any](s iter.Seq[V], transform func(V) R) iter.Seq[R] {\n    return func(yield func(R) bool) {\n        for v := range s {\n            if !yield(transform(v)) {\n                return\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<p>Now, you can use these adapters to create a sequence pipeline:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">type SongSummary struct {\n    Title    string\n    Duration time.Duration\n}\n\nfunc (p *Playlist) SummariesByArtist(artist string) iter.Seq[SongSummary] {\n    filtered := Filter(func(song *PlaylistSong) bool {\n        return song.Song.Artist == artist\n    }, p.All())\n\n    return Map(filtered, func(song *PlaylistSong) SongSummary {\n        return SongSummary{\n            Title:    song.Song.Title,\n            Duration: song.Song.Duration,\n        }\n    })\n}<\/pre>\n\n\n\n<p>This setup allows you to filter and transform your playlist data efficiently, promoting code reuse and clarity.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Benchmarking Maps Iteration Approaches in Go<\/h3>\n\n\n\n<p>When faced with the task of iterating over a map in Go, a question naturally arises: which approach is more performant\u2014using the classic direct map iteration or the newer <code>maps.All<\/code> method introduced in the <code>maps<\/code> package? The <code>maps.All<\/code> method offers a more concise and expressive way to iterate over key-value pairs, but does this added convenience come at a performance cost?<\/p>\n\n\n\n<p>To answer this, I wrote a benchmark test comparing the two methods. The test iterates over a map containing a few constants and measures the time taken by each approach. The benchmark code looks like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">func BenchmarkMapsAll(b *testing.B) {\n    m := map[string]float64{\n        \"\u03b5\": 8.854187817620389e-12,\n        \"\u03c0\":  math.Pi,\n        \"e\":  math.E,\n        \"\u0127\":  1.054571817e-34,\n    }\n    b.ResetTimer()\n    for i := 0; i &lt; b.N; i++ {\n        for _, kv := range maps.All(m) {\n            _ = kv.Key\n            _ = kv.Value\n        }\n    }\n}\n\nfunc BenchmarkDirectMapIteration(b *testing.B) {\n    m := map[string]float64{\n        \"\u03b5\": 8.854187817620389e-12,\n        \"\u03c0\":  math.Pi,\n        \"e\":  math.E,\n        \"\u0127\":  1.054571817e-34,\n    }\n    b.ResetTimer()\n    for i := 0; i &lt; b.N; i++ {\n        for k, v := range m {\n            _ = k\n            _ = v\n        }\n    }\n}<\/pre>\n\n\n\n<p>With this setup, I ran the benchmarks to measure the average time per operation (<code>ns\/op<\/code>) for both methods 5 times. Here are the results:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">BenchmarkMapsAll-16                     11407765               106.1 ns\/op             0 B\/op          0 allocs\/op\nBenchmarkMapsAll-16                     11464362               107.6 ns\/op             0 B\/op          0 allocs\/op\nBenchmarkMapsAll-16                     11203790               106.7 ns\/op             0 B\/op          0 allocs\/op\nBenchmarkMapsAll-16                     11160464               104.8 ns\/op             0 B\/op          0 allocs\/op\nBenchmarkMapsAll-16                     11255577               105.8 ns\/op             0 B\/op          0 allocs\/op\n\nBenchmarkDirectMapIteration-16          11929827               101.9 ns\/op             0 B\/op          0 allocs\/op\nBenchmarkDirectMapIteration-16          12066655                99.67 ns\/op            0 B\/op          0 allocs\/op\nBenchmarkDirectMapIteration-16          12131480                99.49 ns\/op            0 B\/op          0 allocs\/op\nBenchmarkDirectMapIteration-16          11923983                99.21 ns\/op            0 B\/op          0 allocs\/op\nBenchmarkDirectMapIteration-16          11976860               101.1 ns\/op             0 B\/op          0 allocs\/op<\/pre>\n\n\n\n<p>The average time per operation revealed a slight performance advantage for direct map iteration, with an average of 100.27 ns\/op compared to 106.2 ns\/op for <code>maps.All<\/code>. While the difference is small\u2014around 5.93 ns\/op\u2014it does highlight that the direct iteration avoids some minor overhead introduced by maps.All.<\/p>\n\n\n\n<p>This exploration sets the stage for a deeper discussion on trade-offs between performance and code readability, which I\u2019ll explore further below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Disclaimer: The Power of Composition<\/h3>\n\n\n\n<p>While these benchmarks focus purely on performance, it&#8217;s important to note one of the key strengths of <code>maps.All<\/code>: the ability to <strong>chain operations<\/strong>. For scenarios involving filtering, mapping, or combining transformations, <code>maps.All<\/code> provides a much cleaner and more functional approach. This flexibility isn\u2019t reflected in the raw performance numbers but can lead to significantly more maintainable and reusable code, especially in complex workflows.<\/p>\n\n\n\n<p>The choice between the two methods should consider not just speed but also how well the method fits into the overall design of your application.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">That&#8217;s all folks<\/h2>\n\n\n\n<p>In conclusion, exploring Go&#8217;s new <code>range<\/code> with functions has been an enlightening journey into how thoughtful features can enhance code readability, modularity, and maintainability. It\u2019s not just about iterators; it\u2019s about rethinking how we approach iteration itself. By integrating principles like DRY and Open\/Closed, we can build systems that are easier to extend and maintain, even under tight deadlines. So, whether you&#8217;re optimizing playlists or benchmarking maps, Go 1.23 has given us more tools to write cleaner, smarter code\u2014and that\u2019s something worth celebrating. If you haven\u2019t tried it yet, give it a shot. You might just fall in love with Go all over again.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">References:<\/h3>\n\n\n\n<p><a href=\"https:\/\/go.dev\/blog\/range-functions\">https:\/\/go.dev\/blog\/range-functions<\/a><br><a href=\"https:\/\/bitfieldconsulting.com\/posts\/iterators\" target=\"_blank\" rel=\"noopener\" title=\"https:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself\">https:\/\/bitfieldconsulting.com\/posts\/iterators<\/a><br><a href=\"https:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself\" target=\"_blank\" rel=\"noopener\" title=\"https:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself\">https:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself<\/a><br><a href=\"https:\/\/en.wikipedia.org\/wiki\/SOLID\" target=\"_blank\" rel=\"noopener\" title=\"https:\/\/en.wikipedia.org\/wiki\/SOLID\">https:\/\/en.wikipedia.org\/wiki\/SOLID<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Lately, I\u2019ve been exploring some of the newer features in Go, and one that really got me hooked is the ability to use range with&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/douglasgoulart.dev\/?p=87\">Continue reading<span class=\"screen-reader-text\">Iterating Smarter: Unlocking Go&#8217;s New range with Functions<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-87","post","type-post","status-publish","format-standard","hentry","category-blog","entry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=\/wp\/v2\/posts\/87","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=87"}],"version-history":[{"count":4,"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=\/wp\/v2\/posts\/87\/revisions"}],"predecessor-version":[{"id":92,"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=\/wp\/v2\/posts\/87\/revisions\/92"}],"wp:attachment":[{"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=87"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=87"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/douglasgoulart.dev\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=87"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}