Does a subscript setter return a reference to struct?

Maybe someone can explain to me how does the following work:

% xcrun swift repl
  1> struct S { var x: Int }
  2> var a = [S(x:1), S(x:2)]
a: [S] = 2 values {
  [0] = {
    x = 1
  }
  [1] = {
    x = 2
  }
}
  3> a[1] = S(x:5)
  4> print(a)
[__lldb_expr_1.S(x: 1), __lldb_expr_1.S(x: 5)]
  5> a[0].x = 3
  6> print(a)
[__lldb_expr_1.S(x: 3), __lldb_expr_1.S(x: 5)]

I get line 3 but line 5, not so much. a[0] is a get so we get a copy of the stored value (is that assumption wrong?). Did we somehow get a reference to a non-reference type S?

This expands out to something like

var tmp = a[0]
tmp.x = 3
a[0] = tmp
2 Likes

The Swift standard library copy-on-write collections are built on read and modify as an optimization to defend against "accidental quadratics". See this @Ben_Cohen lecture for more background on that.

2 Likes

it would probably be more general to say it expands to

{ $0.x = 3 } (&a[0])
3 Likes

Hmm, is that something I am missing in the Book chapter on subscripts? Is this formally specified somewhere?

I will watch the video recommended above by @vanvoorden but what about this:

 7> struct Foo {
  8.   private var arr = [S(x: 2), S(x: 3)]
  9.
 10.   subscript(_ i: Int) -> S {
 11.     get { arr[i] }
 12.     set(newValue) { arr[i] = newValue }
 13.   }
 14. }
 15.
 16. var foo = Foo()
 17. print(foo)
 18. print(foo[0])
 19. foo[1].x = 10
 20. print(foo)
Foo(arr: [__lldb_expr_1.S(x: 2), __lldb_expr_1.S(x: 3)])
S(x: 2)
Foo(arr: [__lldb_expr_1.S(x: 2), __lldb_expr_1.S(x: 10)])
foo: Foo = {
  arr = 2 values {
    [0] = {
      x = 2
    }
    [1] = {
      x = 10
    }
  }
}

This is my subscript (and not Array's optimization) but it behaves in the same way? Does L19 not call the getter?

I suggest putting print()s inside your getter and setter so you can see when they're called.

3 Likes

I did, and the results were confusing to me prior to asking the question and getting the additional information here.

At any rate, after watching the video in the comment above I did find and read the Modify Accessors evolution pitch and I now understand better what's going on.