<div dir="ltr">This isn'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)">// "123"</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)">// "456"!</span></p></blockquote>
<div><div><br></div><div>Are all of our assumptions correct?</div><div> * Is myVariable'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>) -> <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<Copier: DeepCopier> {</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)">(&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> -> <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)">(&</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() -> <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>) -> <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)"><</span>MyClass<span style="color:rgb(0,0,0)">.</span>Copier<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)">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)">// "123"</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)">// "123"</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)">// "456"</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'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 "const" 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>