<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Generics on WebDevStation</title>
    <link>https://webdevstation.com/tags/generics/</link>
    <description>2 articles tagged Generics — tutorials, code examples and notes from building real systems, newest first.</description>
    <generator>Hugo</generator>
    <language>en</language>
    <managingEditor>Alex</managingEditor>
    <webMaster>Alex</webMaster>
    <copyright>© 2026 WebDevStation</copyright>
    <lastBuildDate>Tue, 01 Sep 2026 18:30:00 +0200</lastBuildDate>
    <atom:link href="https://webdevstation.com/tags/generics/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>What&#39;s New in Go 1.27: Generic Methods, JSON v2 and uuid in the Standard Library</title>
      <link>https://webdevstation.com/posts/whats-new-in-go-1-27/</link>
      <pubDate>Tue, 01 Sep 2026 18:30:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/whats-new-in-go-1-27/</guid>
      <description>Go 1.27 finally adds generic methods to the language, promotes encoding/json/v2 into the standard library, ships a uuid package, and makes goroutine leak profiles…</description>
      <content:encoded><![CDATA[<p>Most Go releases are a handful of library additions and a compiler that got a bit faster. Go 1.27, out in August 2026, is not that release. It changes the language — generic methods are finally here — and it lands two things people have been waiting years for: a second-generation JSON package and a <code>uuid</code> package, both in the standard library.</p>
<h2 id="generic-methods">Generic Methods</h2>
<p>This is the headline, and it is a real language change: <strong>a method declaration may now declare its own type parameters.</strong></p>
<p>Before 1.27, type parameters could only appear on a function or on the type itself. If you wanted a generic helper that logically belonged to a type, your only option was a package-level function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Go 1.26 and earlier: the helper lives in the package namespace,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// even though it only makes sense for *Rand.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">N</span>[<span style="color:#a6e22e">Int</span> <span style="color:#a6e22e">intType</span>](<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Rand</span>, <span style="color:#a6e22e">n</span> <span style="color:#a6e22e">Int</span>) <span style="color:#a6e22e">Int</span>
</span></span></code></pre></div><p>Now it can live where it belongs. <code>math/rand/v2</code> is the standard library&rsquo;s own example — it gained a generic <em>method</em> alongside the existing generic function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Go 1.27</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Rand</span>) <span style="color:#a6e22e">N</span>[<span style="color:#a6e22e">Int</span> <span style="color:#a6e22e">intType</span>](<span style="color:#a6e22e">n</span> <span style="color:#a6e22e">Int</span>) <span style="color:#a6e22e">Int</span>
</span></span></code></pre></div><p>Which means this now compiles:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">NewPCG</span>(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">d</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span> = <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">N</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)  <span style="color:#75715e">// method, inferred as time.Duration</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int32</span>         = <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">N</span>(int32(<span style="color:#ae81ff">100</span>))   <span style="color:#75715e">// same method, different instantiation</span>
</span></span></code></pre></div><p>The practical effect is that you can stop writing <code>pkg.DoThing(x, ...)</code> for operations that are conceptually <code>x.DoThing(...)</code>. Anywhere you have a container, a client, or a builder that needed a package-level generic helper, that helper can now be a method.</p>
<p>Two limits worth knowing before you plan a refactor around it:</p>
<ul>
<li><strong>Interfaces cannot declare type parameters on their methods</strong>, and an interface method cannot be satisfied by a generic method. So this does not give you generic interfaces through the back door; the method set of an interface is still fully concrete.</li>
<li>It is a <em>method</em> type parameter, distinct from the receiver&rsquo;s. <code>func (c *Cache[K, V]) MapTo[T any](...)</code> has three type parameters in play, two from the receiver and one from the method.</li>
</ul>
<p>If you have been writing the awkward package-level workarounds I described in <a href="/posts/example-of-how-generics-simplify-golang/">how Golang generics minimize the amount of code you need to write</a>, this is the release where a lot of that gets to move inside the type.</p>
<p>Two smaller language changes shipped alongside it. A key in a struct literal may now be <strong>any valid field selector</strong>, not just a top-level field name — which makes literals for structs with embedded types far less irritating. And function type inference is <strong>generalised to every context</strong> where a generic function is assigned to, or converted to, a matching function type, so a class of &ldquo;cannot infer&rdquo; errors simply disappears.</p>
<h2 id="encodingjsonv2">encoding/json/v2</h2>
<p>The JSON rewrite is in, as two packages:</p>
<ul>
<li><strong><code>encoding/json/v2</code></strong> — the high-level API you already know, redesigned.</li>
<li><strong><code>encoding/json/jsontext</code></strong> — lower-level syntactic processing, with an <code>Encoder</code> and <code>Decoder</code> that work in terms of <code>Token</code> and <code>Value</code> and hold a state machine guaranteeing the JSON is valid.</li>
</ul>
<p>The v2 entry points take variadic options:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;encoding/json/v2&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">b</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">v</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Deterministic</span>(<span style="color:#66d9ef">true</span>),          <span style="color:#75715e">// stable map ordering</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">OmitZeroStructFields</span>(<span style="color:#66d9ef">true</span>),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>(<span style="color:#a6e22e">data</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">v</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">RejectUnknownMembers</span>(<span style="color:#66d9ef">true</span>),   <span style="color:#75715e">// strict decoding, no silent drops</span>
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>Alongside <code>Marshal</code> and <code>Unmarshal</code> there are <code>MarshalWrite</code> / <code>UnmarshalRead</code> for an <code>io.Writer</code> / <code>io.Reader</code>, and <code>MarshalEncode</code> / <code>UnmarshalDecode</code> for driving a <code>jsontext.Encoder</code> / <code>Decoder</code> directly. That last pair is what you want for streaming, and it is a much better story than v1&rsquo;s <code>json.Decoder</code> ever was.</p>
<p><strong>The defaults are stricter</strong>, which is the whole point. v2 rejects invalid UTF-8 in strings and rejects duplicate names within an object — both of which v1 quietly accepted. That is more interoperable and it closes a genuine class of parser-differential bugs.</p>
<p><code>RejectUnknownMembers(true)</code> deserves a callout for anyone writing an API. v1&rsquo;s default of silently discarding unknown fields is how a typo in a client&rsquo;s request body turns into a field that is quietly zero, and then into a support ticket. Making that an error at the boundary is the same instinct as the sentinel errors in <a href="/posts/error-handling-in-go/">error handling in Go</a>: fail where the information is, not three layers down.</p>
<p><code>encoding/json</code> v1 is not going anywhere, and it gained a matching set of options with names that tell you exactly what they are for — <code>CallMethodsWithLegacySemantics</code>, <code>OmitEmptyWithLegacySemantics</code>, <code>FormatDurationAsNano</code>, <code>ParseTimeWithLooseRFC3339</code> and friends. That is the migration path: move to v2, and where behaviour differs in a way you depend on, opt back into the v1 semantics explicitly rather than discovering the difference in production.</p>
<p>One thing to be deliberate about: <strong>v2&rsquo;s stricter parsing is a behaviour change at your HTTP boundary.</strong> A client that has been sending duplicate keys or malformed UTF-8 and getting away with it will start getting errors. That is correct, and it is still worth knowing before you flip it on a live service. Point a load test at it first — the setup in <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> is enough to see whether real traffic trips the new rules.</p>
<h2 id="a-uuid-package">A uuid Package</h2>
<p>There is now a <code>uuid</code> package in the standard library. The whole API is small enough to quote:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">UUID</span> [<span style="color:#ae81ff">16</span>]<span style="color:#66d9ef">byte</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">New</span>() <span style="color:#a6e22e">UUID</span>                        <span style="color:#75715e">// the recommended default</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewV4</span>() <span style="color:#a6e22e">UUID</span>                      <span style="color:#75715e">// random</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewV7</span>() <span style="color:#a6e22e">UUID</span>                      <span style="color:#75715e">// time-ordered</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Parse</span>(<span style="color:#66d9ef">string</span>) (<span style="color:#a6e22e">UUID</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MustParse</span>(<span style="color:#66d9ef">string</span>) <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Nil</span>() <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Max</span>() <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">String</span>() <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">Compare</span>(<span style="color:#a6e22e">UUID</span>) <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">MarshalText</span>() ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">AppendText</span>([]<span style="color:#66d9ef">byte</span>) ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#f92672">*</span><span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">UnmarshalText</span>([]<span style="color:#66d9ef">byte</span>) <span style="color:#66d9ef">error</span>
</span></span></code></pre></div><p><code>NewV7</code> is the one to reach for when the UUID is a database key. Version 7 is time-ordered, so generated values sort roughly by creation time — which keeps B-tree index inserts near the right-hand edge instead of scattering them across the whole index the way v4 does. If you have ever watched write throughput degrade on a table with a random-UUID primary key, that is the problem v7 exists to solve.</p>
<p><code>Compare</code> returning an <code>int</code> means it drops straight into <code>slices.SortFunc</code>, and the <code>TextMarshaler</code> / <code>TextUnmarshaler</code> implementation means it round-trips through JSON and <code>database/sql</code> without a wrapper type.</p>
<p>The obvious question: does this kill <code>github.com/google/uuid</code>? Not immediately — plenty of code depends on its wider surface. But for the common case of &ldquo;I need a v4 or v7 UUID, and I need to parse one&rdquo;, that is now a dependency you can delete.</p>
<h2 id="goroutine-leak-profiles-generally-available">Goroutine Leak Profiles, Generally Available</h2>
<p>The <code>goroutineleak</code> profile, experimental in 1.26, is now GA in <code>runtime/pprof</code> and exposed at <code>/debug/pprof/goroutineleak</code>.</p>
<p>It reports goroutines blocked on a concurrency primitive that <strong>cannot possibly become unblocked</strong>. The detection is clever: it piggybacks on the garbage collector. If goroutine G is blocked on primitive P, and P is unreachable from any runnable goroutine — or from any goroutine those could unblock — then nothing can ever signal P, so G is leaked.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#a6e22e">_</span> <span style="color:#e6db74">&#34;net/http/pprof&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// then:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//   go tool pprof http://localhost:6060/debug/pprof/goroutineleak</span>
</span></span></code></pre></div><p>The reachability trick is also the limitation, and the release notes say so plainly: leaks caused by blocking on a primitive that is still reachable through a global, or through a runnable goroutine&rsquo;s locals, will not be detected. It catches a large class, not all of them.</p>
<p>Still, this is the first tool that turns &ldquo;we think we have a goroutine leak somewhere&rdquo; into a list. The failure modes it finds are exactly the ones from <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a> — a worker blocked forever on a send to a channel nobody is reading — and the ones a missing <code>&lt;-ctx.Done()</code> produces, which I wrote about in <a href="/posts/understanding-golang-context/">understanding Golang context</a>.</p>
<h2 id="free-performance">Free Performance</h2>
<p>The compiler now emits calls to <strong>size-specialised allocation routines</strong>, cutting the cost of small allocations (under 80 bytes) by up to 30%. The release notes are honest about what that means end to end: roughly <strong>1% on allocation-heavy programs</strong>, and about 60 KB more binary, regardless of workload.</p>
<p>One percent for a recompile is a good trade. If it causes you trouble, <code>GOEXPERIMENT=nosizespecializedmalloc</code> turns it off at build time — but note that escape hatch is expected to be <strong>removed in Go 1.28</strong>, so treat it as a window to file a bug, not a setting to keep.</p>
<h2 id="smaller-things-worth-knowing">Smaller Things Worth Knowing</h2>
<table>
	<thead>
			<tr>
					<th>Change</th>
					<th>Why you care</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>strings.CutLast(s, sep)</code></td>
					<td><code>Cut</code> from the right. The one you kept writing by hand with <code>LastIndex</code>.</td>
			</tr>
			<tr>
					<td><code>testing/synctest.Sleep(d)</code></td>
					<td>Sleep inside a synthetic-time bubble, without the awkward dance around the fake clock.</td>
			</tr>
			<tr>
					<td><code>hash/maphash.Hasher[T]</code> and <code>ComparableHasher[T]</code></td>
					<td>A real interface for hashing your own types — building a custom hash map stops meaning &ldquo;reimplement hashing&rdquo;.</td>
			</tr>
			<tr>
					<td><code>net/http.Server.MaxHeaderValueCount</code> (default 500)</td>
					<td>A cap on header value count, defending against header-flood requests.</td>
			</tr>
			<tr>
					<td><code>net/http.Server.DisableClientPriority</code></td>
					<td>Ignore client-supplied HTTP/2 priority hints.</td>
			</tr>
			<tr>
					<td><code>database/sql/driver.RowsColumnScanner</code></td>
					<td>Drivers can take over per-column scanning, which is how you avoid a round trip through <code>interface{}</code> for every value.</td>
			</tr>
			<tr>
					<td><code>go doc pkg@version</code></td>
					<td>Read the docs for a <em>specific</em> version without changing your module.</td>
			</tr>
			<tr>
					<td><code>go test</code> runs <code>stdversion</code> by default</td>
					<td>Catches use of stdlib symbols newer than the Go version your <code>go.mod</code> declares.</td>
			</tr>
	</tbody>
</table>
<p>That last one is quietly excellent. It is the failure mode where your code builds on your machine and breaks on a builder pinned to an older Go — the same class of problem I hit deploying this blog, where a template function that did not exist in the CI toolchain failed a build that was green locally.</p>
<h2 id="two-for-later">Two for Later</h2>
<p><strong><code>crypto/mldsa</code></strong> implements ML-DSA, the post-quantum signature scheme standardised as FIPS 204, with the three parameter sets <code>MLDSA44()</code>, <code>MLDSA65()</code> and <code>MLDSA87()</code>. Unless you have a compliance requirement you will not touch it this year, but it being in the standard library is what makes migration a normal task rather than a project.</p>
<p><strong><code>simd</code></strong> is an experimental package behind <code>GOEXPERIMENT=simd</code>, providing portable, vector-size-agnostic SIMD types like <code>Int8s</code> and <code>Float32s</code> that use hardware instructions where available. Experimental means experimental — but portable SIMD in Go is a genuinely interesting direction.</p>
<h2 id="should-you-upgrade">Should You Upgrade?</h2>
<p>Yes, and the calculus is unusually simple:</p>
<ul>
<li>The Go 1 compatibility promise holds; almost everything compiles unchanged.</li>
<li>You get ~1% on allocation-heavy code for a recompile.</li>
<li><code>goroutineleak</code> is worth the upgrade on its own for any service running long-lived goroutines.</li>
<li>Generic methods and JSON v2 are opt-in. Nothing forces you to rewrite anything.</li>
</ul>
<p>The only change that needs a moment&rsquo;s thought is JSON v2&rsquo;s stricter parsing, and only if you adopt it at a public boundary. Everything else is additive.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Generic methods close a gap that has been awkward since generics landed in 1.18, and they will quietly improve a lot of library APIs over the next year. JSON v2 is the rarer thing: a second attempt at a core package that gets to fix the defaults, with an explicit escape hatch back to the old behaviour. And a <code>uuid</code> package removes a dependency from nearly every service I have written.</p>
<p>Not bad for a release the notes describe as &ldquo;mostly implementation&rdquo;.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Example of how Golang generics minimize the amount of code you need to write</title>
      <link>https://webdevstation.com/posts/example-of-how-generics-simplify-golang/</link>
      <pubDate>Thu, 09 Jun 2022 15:41:52 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/example-of-how-generics-simplify-golang/</guid>
      <description>Explore practical examples of Go 1.18 generics in action by refactoring caching logic in a real-world application to write cleaner, more maintainable code with less…</description>
      <content:encoded><![CDATA[<p>I guess that almost everyone in the go community was exciting when Go 1.18 was released, especially because of generics.
Some days ago I decided to try generics in the real-world application, by refactoring some of its pieces, related to a caching logic.</p>
<p>In our web application, we have multiple resolvers that execute some sql queries and return data in different types. Obviously,
we want to prevent the database overloading by caching the same responses.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyExampleType1 type to serve example response 1.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyExampleType1</span> <span style="color:#66d9ef">struct</span> {}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyExampleType2 type to serve example response 2.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyExampleType2</span> <span style="color:#66d9ef">struct</span> {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyCachedResolver1 checks if there are any cached data by specific key.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If nothing in cache, queries the database and adds result to the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver1</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result&#34;</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">FetchSomethingHeavyFromDB</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> []<span style="color:#a6e22e">MyExampleType1</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyCachedResolver2 checks if there are any cached data by specific key.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If nothing in cache, queries the database and adds result to the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver2</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_2&#34;</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">FetchSomethingEvenMoreHeavierFromDB</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> []<span style="color:#a6e22e">MyExampleType2</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>As you can see, there is some repetitive code that should be written due to the different types <code>MyExampleType1</code> and <code>MyExampleType2</code>.</p>
<p>Now, let&rsquo;s see how we can improve this situation by using generics. I&rsquo;m going to write a function <code>WithCache()</code> which will be responsible for
setting and getting data to/from redis cache.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>    <span style="color:#75715e">// WithCache adds exec function result into the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Pay attention to &#34;T any&#34; that allows us to pass any type to this function.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">WithCache</span>[<span style="color:#a6e22e">T</span> <span style="color:#66d9ef">any</span>](<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">key</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">exec</span> <span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>), <span style="color:#a6e22e">ttl</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>) (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    	<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">result</span> <span style="color:#a6e22e">T</span>
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// exec() is a function that should be executed to fetch needed data.</span>
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">exec</span>()
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    		}
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">jsonValue</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    		}
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">jsonValue</span>, <span style="color:#a6e22e">ttl</span>)
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    	}
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> <span style="color:#a6e22e">T</span>
</span></span><span style="display:flex;"><span>    	<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    	}
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>After that our resolvers could be refactored into this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>     <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver1</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_1&#34;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">WithCache</span>[[]<span style="color:#a6e22e">MyExampleType1</span>](<span style="color:#a6e22e">redisClient</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#66d9ef">func</span>() ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>		 <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">FetchSomethingHeavyFromDB</span>()
</span></span><span style="display:flex;"><span>	   }, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>     }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver2</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_2&#34;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">WithCache</span>[[]<span style="color:#a6e22e">MyExampleType2</span>](<span style="color:#a6e22e">redisClient</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#66d9ef">func</span>() ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>		 <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">FetchSomethingEvenMoreHeavierFromDB</span>()
</span></span><span style="display:flex;"><span>	   }, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>     }
</span></span></code></pre></div><p>Now it looks much better and clearer!</p>
<p>The caching code this example refactors is the one from <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto — the most performant concurrent cache library for Go</a>, if you want the full context. For another place where a little type machinery removes a lot of duplication, see <a href="/posts/implementing-enums-in-golang/">implementing enums in Golang</a>. And since Go 1.27 a method can carry its own type parameters, which removes the package-level helper this article works around — <a href="/posts/whats-new-in-go-1-27/">what&rsquo;s new in Go 1.27</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
  </channel>
</rss>
