<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Go on Louis&#39;s blog</title>
        <link>https://blog.louishhy.com/tags/go/</link>
        <description>Recent content in Go on Louis&#39;s blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <copyright>Louis Huang</copyright>
        <lastBuildDate>Tue, 02 Jun 2026 11:59:11 +0000</lastBuildDate><atom:link href="https://blog.louishhy.com/tags/go/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Go: Should You Return a Value or Pointer for Structs?</title>
        <link>https://blog.louishhy.com/post/go-should-you-return-a-value-or-pointer-for-structs/</link>
        <pubDate>Tue, 02 Jun 2026 11:59:11 +0000</pubDate>
        
        <guid>https://blog.louishhy.com/post/go-should-you-return-a-value-or-pointer-for-structs/</guid>
        <description>&lt;p&gt;Consider the following Go code,
which one would you prefer?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SomeStruct&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ... fields
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Choice 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewSomeStruct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SomeStruct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SomeStruct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// ... fields
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Choice 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewSomeStruct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;SomeStruct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;SomeStruct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// ... fields
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;why&#34;&gt;Why
&lt;/h2&gt;&lt;p&gt;I have been learning Go recently and, as a heavy user in other more high-level programming languages like Python,
I find it quite intriguing to see Go throwing around pointers everywhere.&lt;/p&gt;
&lt;p&gt;For those from the C/Cpp background it might seem not that interesting though.&lt;/p&gt;
&lt;p&gt;The question I wanted to ask is, when returning structs, whether we should prefer or default to any one of those:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Return pointers&lt;/li&gt;
&lt;li&gt;Return values
so to somehow lower the mental burden.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;searching-for-answer&#34;&gt;Searching for answer
&lt;/h2&gt;&lt;p&gt;Unfortunately up till now I haven&amp;rsquo;t seen official guidance on this topic.
There are official discussions about &lt;a class=&#34;link&#34; href=&#34;https://go.dev/wiki/CodeReviewComments#receiver-type&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;receiver types&lt;/a&gt; (&lt;em&gt;When in doubt, use pointer receiver&lt;/em&gt;) and
&lt;a class=&#34;link&#34; href=&#34;https://go.dev/wiki/CodeReviewComments#pass-values&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;function arguments&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, to consider whether to use values or pointers, we can take a look at the two sides:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using value&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Copying is fast when structures are small.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slices&lt;/code&gt;, &lt;code&gt;maps&lt;/code&gt; are already pointer-ish, so no need to return by pointer. &lt;a class=&#34;link&#34; href=&#34;https://research.swtch.com/godata&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Go data structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A value is more likely to live on the stack, which is good for performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Using pointers&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Faster, if the struct is large.
&lt;ul&gt;
&lt;li&gt;Comes with caveats though, sometimes it will trigger heap allocations and garbage collection. Because you use pointers, they may be subject to escape analysis which escapes to the heap, causing GC pressures and performance issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Some concurrency-related structures like &lt;code&gt;sync.Mutex&lt;/code&gt; are NOT MEANT TO BE COPIED.&lt;/li&gt;
&lt;li&gt;You may need the &lt;code&gt;Optional&lt;/code&gt; semantic, in that case pointers gives you the ability to return &lt;code&gt;nil&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;my-rule-of-thumb-identity&#34;&gt;My rule-of-thumb: Identity
&lt;/h2&gt;&lt;p&gt;I decide to stick to the heuristics that I derived, in my &lt;strong&gt;current stage&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the returned value does not have &lt;strong&gt;identity&lt;/strong&gt; and is not too big, return values.&lt;/li&gt;
&lt;li&gt;Else, default to using pointers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If performance proves that pointers are a problem, then do the optimization.&lt;/p&gt;
&lt;p&gt;What I say identity is that, for example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Point&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Y&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;then, semantically,&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Point&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Point&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those two points are &amp;ldquo;identity-less&amp;rdquo;, they basically represent the same thing.&lt;/p&gt;
&lt;p&gt;But, if we are talking about&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DBConnection&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;socket&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;net&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Conn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;then, each of those structs &lt;strong&gt;should&lt;/strong&gt; represent different resources, and here pointer semantics wins.&lt;/p&gt;
&lt;p&gt;You can think of why mutex locks should not be copied, since one mutex lock should relate to only one resource.
A copied mutex would create two lock states that appear to guard the same thing but are actually independent.&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
