Is there a way to make the for .. in
loop return references to the entries of a collection instead of copies?
Say I have an array points
of CGPoint
objects and I want to loop over them and pass each point to a function adjustPoint
that can modify the point using an inout
parameter.
Now doing the following doesn't work, since the for .. in
loop returns the points as immutable / mutable (depending on whether or not I use var
) copies of the actual points in the array:
for var point in points {
adjustPoint(point: &point) // This function only changes the copy
}
Currently, the only way I see to do this is to loop over the index:
for i in 0..<points.count {
adjustPoint(point: &points[i])
}
Is this really the only way or is it also possible with a for .. in
loop?
Note: I've read this question which is from quite some time ago (Swift 1 I believe) so I thought maybe they've changed something in the meantime: turn for in loops local variables into mutable variables
To pass parameter by reference to a Swift function, define this parameter with inout keyword, and use preface the parameter in function call with ampersand (&).
Structs in Swift are passed by value, but you can use the inout modifier to modify your array (see answers below). Classes are passed by reference.
Array Type Shorthand Syntax The type of a Swift array is written in full as Array<Element> , where Element is the type of values the array is allowed to store. You can also write the type of an array in shorthand form as [Element] .
In Swift, the break statement is used to immediately end the execution of a loop, a switch statement, an if statement of a do statement. As such, the break statement consists of a single break keyword followed optionally by a (you guessed it) statement label!
So the basic answer for your original for
loop question is: no. The for...in
is designed to give you copies of value types. It's a forced-functional programming style as you said yourself in the comments.
To mutate an array you must say array[index]
in some fashion or another and now you're referring to the original value and can mutate it. The trick is finding an expressive way that prevents common errors. The four techniques I'm advocating below are:
extension
so you DRY throughout the codeindices
not a manual range which is also error prone (...
vs. ..<
)&
(see #1)This is probably most in keeping with the spirit of Swift, that is, quirky, verbose, and more nettlesome than you'd want, but ultimately very expressive and powerful with the proper layers in place:
import Foundation
import CoreGraphics
protocol Pointy {
var x: CGFloat { get set }
var y: CGFloat { get set }
func adjustBy(amount: CGFloat) -> CGPoint
mutating func adjustInPlace(amount: CGFloat) -> Void
}
extension CGPoint: Pointy {
func adjustBy(amount: CGFloat) -> CGPoint {
return CGPoint(x: self.x + amount, y: self.y + amount)
}
mutating func adjustInPlace(amount: CGFloat) -> Void {
x += amount
y += amount
}
}
extension Array where Element: Pointy {
func adjustBy(amount: CGFloat) -> Array<Pointy> {
return self.map { $0.adjustBy(amount: amount) }
}
mutating func adjustInPlace(amount: CGFloat) {
for index in self.indices {
// mysterious chunk of type calculus: need "as! Element" -- https://forums.developer.apple.com/thread/62164
self[index].adjustInPlace(amount: amount) // or self[index] = (self[index].adjustBy(amount: amount)) as! Element
}
}
}
// Hide the above in a Util.swift that noone ever sees.
// AND NOW the true power shows
var points = [ CGPoint(x: 3.0, y: 4.0) ]
points.adjustInPlace(amount: 7.5)
points.forEach { print($0) }
// outputs (10.5, 11.5)
let adjustedPoints = points.adjustBy(amount: 7.5) // Original unchanged
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With