<div dir="ltr">This isn&#39;t a proposal, more something to get a discussion started.<div><br></div><div>TL;DR: You can make a struct look like a class, a class look like a struct, a mutable type appear immutable.<br><div><br></div><div>I think that Swift is doing these things as it should. However I think that there is room for mistakes, and it would be nice to be aware of those mistakes and guard against them.<br></div><div><div><br></div><div>Consider the following code:<div><br></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(187,44,162)">private<span style="color:rgb(0,0,0)"> </span>class<span style="color:rgb(0,0,0)"> MyClass {</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> value: <span style="color:rgb(112,61,170)">Int</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(value: <span style="color:rgb(112,61,170)">Int</span>) { <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">value</span> = value }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">struct</span> MyValue {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">private</span> <span style="color:rgb(187,44,162)">let</span> reference: <span style="color:rgb(79,129,135)">MyClass</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(value: <span style="color:rgb(112,61,170)">Int</span>) {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">reference</span> = <span style="color:rgb(79,129,135)">MyClass</span>(value: value)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> value: <span style="color:rgb(112,61,170)">Int</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">get</span> { <span style="color:rgb(187,44,162)">return</span> <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">reference</span>.<span style="color:rgb(79,129,135)">value</span> }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">set</span> { <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">reference</span>.<span style="color:rgb(79,129,135)">value</span> = newValue }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">var</span> myVariable = <span style="color:rgb(79,129,135)">MyValue</span>(value: <span style="color:rgb(39,42,216)">123</span>)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(61,29,129)">print</span><span style="color:rgb(0,0,0)">(</span>myVariable<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)">) </span><span style="color:rgb(0,132,0)">// &quot;123&quot;</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;min-height:13px"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">let</span> myOtherVariable = <span style="color:rgb(79,129,135)">myVariable</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;min-height:13px"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)">myVariable<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)"> = </span><span style="color:rgb(39,42,216)">456</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">



















</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(61,29,129)">print</span><span style="color:rgb(0,0,0)">(</span>myOtherVariable<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)">) </span><span style="color:rgb(0,132,0)">// &quot;456&quot;!</span></p></blockquote>







<div><div><br></div><div>Are all of our assumptions correct?</div><div> * Is myVariable&#39;s a value type? probably</div><div> * Is myVariable copy-on-write? perhaps</div><div> * Should we be able to write to myVariable.value despite reference being let? probably</div><div> * Is this code unsafe, or misleading? yes. Can we prevent that? maybe</div><div><br></div><div>Some other code, this is not perfect but it mostly makes MyValue copy-on-write:</div><div><br></div></div></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">protocol</span> DeepCopier {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">typealias</span> ObjectType: AnyObject</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">static</span> <span style="color:rgb(187,44,162)">func</span> deepCopy(that: <span style="color:rgb(112,61,170)">ObjectType</span>) -&gt; <span style="color:rgb(112,61,170)">ObjectType</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">struct</span> UniqueReference&lt;Copier: DeepCopier&gt; {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">private</span> <span style="color:rgb(187,44,162)">var</span> _reference: <span style="color:rgb(112,61,170)">Copier</span>.<span style="color:rgb(112,61,170)">ObjectType</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(<span style="color:rgb(187,44,162)">var</span> reference: <span style="color:rgb(112,61,170)">Copier</span>.<span style="color:rgb(112,61,170)">ObjectType</span>) {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(61,29,129)"><span style="color:rgb(0,0,0)">        </span><span style="color:rgb(187,44,162)">if</span><span style="color:rgb(0,0,0)"> </span>isUniquelyReferencedNonObjC<span style="color:rgb(0,0,0)">(&amp;reference) {</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">            <span style="color:rgb(79,129,135)">_reference</span> = reference</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">else</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">            <span style="color:rgb(79,129,135)">_reference</span> = Copier.<span style="color:rgb(49,89,93)">deepCopy</span>(reference)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> reference: <span style="color:rgb(112,61,170)">Copier</span>.<span style="color:rgb(112,61,170)">ObjectType</span> { <span style="color:rgb(187,44,162)">return</span> <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">_reference</span> }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">mutating</span> <span style="color:rgb(187,44,162)">func</span> update(block: <span style="color:rgb(112,61,170)">Copier</span>.<span style="color:rgb(112,61,170)">ObjectType</span> -&gt; <span style="color:rgb(112,61,170)">Void</span>) {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(61,29,129)"><span style="color:rgb(0,0,0)">        </span><span style="color:rgb(187,44,162)">if</span><span style="color:rgb(0,0,0)"> !</span>isUniquelyReferencedNonObjC<span style="color:rgb(0,0,0)">(&amp;</span><span style="color:rgb(187,44,162)">self</span><span style="color:rgb(0,0,0)">.</span><span style="color:rgb(79,129,135)">_reference</span><span style="color:rgb(0,0,0)">) {</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">            <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">_reference</span> = Copier.<span style="color:rgb(49,89,93)">deepCopy</span>(<span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">_reference</span>)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        block(<span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">_reference</span>)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;min-height:13px"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(112,61,170)"><span style="color:rgb(187,44,162)">private</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(187,44,162)">class</span><span style="color:rgb(0,0,0)"> MyClass: </span>NonObjectiveCBase<span style="color:rgb(0,0,0)"> {</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> value: <span style="color:rgb(112,61,170)">Int</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(value: <span style="color:rgb(112,61,170)">Int</span>) { <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">value</span> = value }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">func</span> deepCopy() -&gt; <span style="color:rgb(79,129,135)">MyClass</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">return</span> <span style="color:rgb(79,129,135)">MyClass</span>(value: <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">value</span>)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">private</span> <span style="color:rgb(187,44,162)">struct</span> Copier: <span style="color:rgb(79,129,135)">DeepCopier</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">static</span> <span style="color:rgb(187,44,162)">func</span> deepCopy(that: <span style="color:rgb(79,129,135)">MyClass</span>) -&gt; <span style="color:rgb(79,129,135)">MyClass</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">            <span style="color:rgb(187,44,162)">return</span> <span style="color:rgb(79,129,135)">MyClass</span>(value: that.<span style="color:rgb(79,129,135)">value</span>)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;min-height:13px"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">struct</span> MyValue {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(0,0,0)">    </span><span style="color:rgb(187,44,162)">private</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(187,44,162)">var</span><span style="color:rgb(0,0,0)"> myReference: </span>UniqueReference<span style="color:rgb(0,0,0)">&lt;</span>MyClass<span style="color:rgb(0,0,0)">.</span>Copier<span style="color:rgb(0,0,0)">&gt;</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(value: <span style="color:rgb(112,61,170)">Int</span>) {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">myReference</span> = <span style="color:rgb(79,129,135)">UniqueReference</span>(reference: <span style="color:rgb(79,129,135)">MyClass</span>(value: value))</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> value: <span style="color:rgb(112,61,170)">Int</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(0,0,0)">        </span><span style="color:rgb(187,44,162)">get</span><span style="color:rgb(0,0,0)"> { </span><span style="color:rgb(187,44,162)">return</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(187,44,162)">self</span><span style="color:rgb(0,0,0)">.</span>myReference<span style="color:rgb(0,0,0)">.</span>reference<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">set</span> { <span style="color:rgb(187,44,162)">self</span>.<span style="color:rgb(79,129,135)">myReference</span>.<span style="color:rgb(49,89,93)">update</span> { $0.<span style="color:rgb(79,129,135)">value</span> = newValue } }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;min-height:13px"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">var</span> myVariable = <span style="color:rgb(79,129,135)">MyValue</span>(value: <span style="color:rgb(39,42,216)">123</span>)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(61,29,129)">print</span><span style="color:rgb(0,0,0)">(</span>myVariable<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)">) </span><span style="color:rgb(0,132,0)">// &quot;123&quot;</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;min-height:13px"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">let</span> myOtherVariable = <span style="color:rgb(79,129,135)">myVariable</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;min-height:13px"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)">myVariable<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)"> = </span><span style="color:rgb(39,42,216)">456</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(61,29,129)">print</span><span style="color:rgb(0,0,0)">(</span>myOtherVariable<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)">) </span><span style="color:rgb(0,132,0)">// &quot;123&quot;</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">





















































</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(61,29,129)">print</span><span style="color:rgb(0,0,0)">(</span>myVariable<span style="color:rgb(0,0,0)">.</span>value<span style="color:rgb(0,0,0)">) </span><span style="color:rgb(0,132,0)">// &quot;456&quot;</span></p></blockquote><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(187,44,162)">

















































</p></div><div><br></div><div>Even more code, I don&#39;t trust anything any more:</div><div><br></div></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">struct</span> MyValue {</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(value: <span style="color:rgb(112,61,170)">Int</span>) { }</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> value: <span style="color:rgb(112,61,170)">Int</span> {</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">get</span> { <span style="color:rgb(187,44,162)">return</span> <span style="color:rgb(112,61,170)">Int</span>(<span style="color:rgb(61,29,129)">arc4random</span>()) }</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">        <span style="color:rgb(187,44,162)">set</span> { }</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    }</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p></div></div></blockquote><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">





</p></div><div><br></div><div>The public interface for MyValue in all these cases looks totally innocent:</div><div><br></div></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">struct</span> MyValue {</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(value: <span style="color:rgb(112,61,170)">Int</span>)</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> value: <span style="color:rgb(112,61,170)">Int</span></p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p></div></div></blockquote><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"></p></div><div><br></div><div>Protocols are the same, they provide no guarantees about immutability, or deep-copy-on-write:</div><div><br></div></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(187,44,162)">protocol</span> SomeValue {</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">init</span>(value: <span style="color:rgb(112,61,170)">Int</span>)</p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">    <span style="color:rgb(187,44,162)">var</span> value: <span style="color:rgb(112,61,170)">Int { get }</span></p></div></div><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo">}</p></div></div></blockquote><div><div><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"></p></div><div><br></div><div>The results may be a bit surprising.</div><div><br></div><div> * Perhaps we could introduce a &quot;const&quot; keyword like in other languages, so the class cannot be mutated (how do we even scope this?).</div><div> * Perhaps we could prevent structures from holding references (no classes, non-pure blocks, no arc4random, no AnySequence, ...).<br></div><div> * Perhaps we can add a @copy_on_write keyword on structures that enforces certain guarantees? (how?)</div><div> * Perhaps add language support for DeepCopier (with a better name, probably a keyword rather than a protocol), that allows anything to be copied like a structure (my preference).</div><div><br></div><div><br></div></div></div></div></div>