We already know multiple optional bindings can be used in a single if/guard statement by separating them with commas, but not with &&
e.g.
In Swift 3, the where
keyword in condition clauses were replaced by a comma instead.
So a statement like if 1 == 1, 2 == 2 {}
is saying "if 1 equals 1 where 2 equals 2..."
It's probably easiest to read a conditional statement with an &&
instead of a ,
, but the results are the same.
You can read more about the details of the change in Swift 3 in the Swift Evolution proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md
When it comes to evaluating boolean comma-separated conditions, the easies way to think of a comma is a pair or brackets. So, if you have
if true, false || true {}
It gets transformed into
if true && (false || true) {}
When pattern matching a associated value in a switch, it matters when using a ,
or &&
. One compiles, the other won't (Swift 5.1). This compiles (&&
):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10 && integer < 10:
print("hi")
default:
fatalError()
}
}
This won't (,
):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10, integer < 10:
print("hi")
default:
fatalError()
}
}
https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_condition-list
The Swift grammar says that the if
statement condition-list
can be composed by multiple condition
separated by commas ,
A simple condition can be a boolean expression
, a optional-binding-condition
or other things:
So, using the comma to separate multiple expressions is perfectly fine.
Here is a case where they are sufficiently different as to require the ,
. The following code will yield
'self' captured by a closure before all members were initialized
class User: NSObject, NSCoding {
public var profiles: [Profile] = []
private var selectedProfileIndex : Int = 0
public required init?(coder aDecoder: NSCoder) {
// self.profiles initialized here
let selectedIndex : Int = 100
if 0 <= selectedIndex && selectedIndex < self.profiles.count { <--- error here
self.selectedProfileIndex = selectedIndex
}
super.init()
}
...
}
This is due to the definition of && on Bool:
static func && (lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool
The selectedIndex < self.profiles.count
on the RHS is caught in a closure.
Changing the &&
to ,
will get rid of the error. Unfortunately, I'm not sure how ,
is defined, but I thought that this was interesting.
Actually the result is not the same. Say that you have 2 statements in an if and && between them. If in the first one you create a let using optional binding, you won't be able to see it in the second statement. Instead, using a comma, you will.
Comma example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)), cell.isSelected {
//Everything ok
}
&& Example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)) && cell.isSelected {
//ERROR: Use of unresolved identifier 'cell'
}
Hope this helps.