I’ve been learning swift and encountered a question about memory safety. The +=
operator takes an inout
parameter on the left, which should have write access over the entire function call. And it do something like left = right+left
within its implementation. It seems to be an overlapping of write and read accesses. How comes this doesn’t violate memory safety?
Edit: According to The Swift Programming Language, it can happen in a single thread:
However, the conflicting access discussed here can happen on a single thread and doesn’t involve concurrent or multi-threaded code.
Elaborate:
Here are two examples from The Swift Programming Language (Swift 4.1 beta). I’m confused how this custom +=
implementation in a struct Vector2D
is okay:
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
When this is not:
var stepSize = 1
func incrementInPlace(_ number: inout Int) {
number += stepSize
}
incrementInPlace(&stepSize)
// Error: conflicting accesses to stepSize
Further edit:
I think my problem really is that += as a func, specifically when used
stepSize += stepSize
Or with custom implementation:
var vector = Vector2D(x: 3.0, y: 1.0)
vector += vector
This doesn’t have any error. But the func takes an inout from the left and thus have a long-term write access to “step”, then if the right also passed in “step”, I’m confused how that isn’t an instant read acess of “step” overlapping with long term write of “step.” Or is it only a problem when you pass in the same instance for two inout parameters, but nor one inout and one regular?
I know you've got it, but a clarification for future readers; in your comments, you said:
No, that alone is not sufficient. As the Memory Safety chapter says, this problem manifests itself only when:
Consider:
The
foo = foo + 1
is not a problem (nor wouldfoo += 1
; nor wouldfoo += foo
) because constitutes a series of "instantaneous" accesses. So although we have (to use your phrase) "code changing self by reading self first", it is not a problem because their durations do not overlap.The problem only manifests itself when you're dealing with "long-term" accesses. As that guide goes on to say:
So, consider your second example:
In this case, you have a long-term access to whatever
number
references. When you invoke it with&stepSize
, that means you have a long-term access to the memory associated withstepSize
and thusnumber += stepSize
means that you're trying to accessstepSize
while you already have a long-term access to it.