Reputation: 221
Referring to the official tutorial, there is memory conflict in swift, however, based on the knowledge of my javascript, there is not a memory conflict, the code below will always be right.
func balance(_ x: inout Int, _ y: inout Int) {
let sum = x + y
x = sum / 2
y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore) // OK
balance(&playerOneScore, &playerOneScore) // Error: conflicting accesses to playerOneScore
Upvotes: 2
Views: 275
Reputation: 539985
From Memory Safety:
Specifically, a conflict occurs if you have two accesses that meet all of the following conditions:
- At least one is a write access or a nonatomic access.
- They access the same location in memory.
- Their durations overlap.
And later:
A function has long-term write access to all of its in-out parameters. The write access for an in-out parameter starts after all of the non-in-out parameters have been evaluated and lasts for the entire duration of that function call.
Therefore, passing the the same variable as in-out parameter to
func balance(_ x: inout Int, _ y: inout Int)
counts as overlapping write accesses to the same memory location, and therefore as a conflict.
For the rationale and more details, see SE-0176 Enforce Exclusive Access to Memory, which has been implemented in Swift 4. In particular, exclusive memory access is enforced to
As an example, the following two seemingly equivalent functions are in fact not equivalent if the same variable is passed as an inout argument, so that mutating x
can affect y
and vice versa:
func balance(_ x: inout Int, _ y: inout Int) {
let sum = x + y
x = sum / 2
y = sum - x
}
func balance(_ x: inout Int, _ y: inout Int) {
x = (x + y) / 2
y = (x + y) / 2 - x
}
Another example:
var global = 0
func foo(_ x: inout Int, _ y: inout Int) {
x += y
global = y
}
If mutating x
might modify y
and vice versa, the compiler cannot optimize the code to load the value of y
in a register first, i.e. perform the equivalent of
func foo(_ x: inout Int, _ y: inout Int) {
let savedY = y
x += savedY
global = savedY
}
It has also been discussed (see Eliminating non-instantaneous accesses?) to eliminate long-term access by making temporary copies for the duration of the function call which are assigned back when the function returns, i.e. do something like
func balance(_ x: inout Int, _ y: inout Int) {
var (localX, localY) = (x, y)
let sum = localX + localY
localX = sum / 2
localY = sum - localX
(x, y) = (localX, localY)
}
This idea was discarded because it is bad for the performance, even for “simple types” but much worse for “container types” like Array
.
Upvotes: 5