Reputation: 393
In "The Swift Programming Language (Swift 5.6 beta)" by Apple, in the Language Reference/Statements section, there is a formal definition of a guard statement
guard <condition> else {
<statements>
}
where, I quote “...The value of any condition in a guard statement must be of type Bool or a type bridged to Bool. The condition can also be an optional binding declaration, as discussed in Optional Binding...”
What does it mean for a type to be bridged to Bool
(or to whatever other type T
assuming bridging to T
is possible)? Is this something specific to Bool
, like an implicit cast? Is it a built-in or can one generally bridge to any T
by following a formal procedure.
I've searched far and wide but have not been able to find a formal-ish description of bridging in the same publication or elsewhere (if one exists please point it out to me).
Upvotes: 1
Views: 724
Reputation: 29843
The term "bridging" for Swift comes from the older Cocoa frameworks concept of Toll-Free Bridging. In Objective-C, certain Foundation class types have the same underlying layout as their CoreFoundation counterpart types such that it's possible to cast between one type and another and have them work identically: you can create a CoreFoundation object in C, then pass it to Objective-C and call methods on it — or you can create a Foundation object in Objective-C, and pass that object to a function in C expecting the CoreFoundation equivalent. Toll-Free Bridging allows you to cross the "bridge" between languages/levels of abstraction, without paying a "toll" to convert between the types (since the cast is direct in memory, there's no runtime cost to converting these types).
Not all types are like this, and these types are designed specifically to behave in this way.
The concept was carried over to Swift, where certain Foundation types can be similarly "bridged" to Swift types, such as String
↔︎ NSString
, Array
↔︎ NSArray
, Dictionary
↔︎ NSDictionary
, etc. The bridging isn't necessarily always "toll-free" (as in, the objects can be cast directly in memory without their underlying representation being changed), but close enough.
The statement, then, saying
The value of the condition must be of type
Bool
or a type bridged toBool
.
means that the condition must be a Bool
, or a type which can be converted directly into a Bool
in this way.
The tricky thing about this description is that there isn't a type which can be unconditionally bridged into a Bool
from Objective-C anymore. You can bridge an NSNumber
to a Bool
with a conditional cast (e.g. let b = NSNumber(value: true) as? Bool
), but that's not enough to allow you to substitute an NSNumber
in place of the condition:
while NSNumber(value: true) as? Bool { // error: Value of type 'Bool?' must be unwrapped to a value of type 'Bool'
print("Hello")
}
I believe this sentence is a holdover from previous versions of the language where the rules are a little bit different — and is sort of confusing now.
Is it a built-in or can one generally bridge to any T by following a formal procedure.
T
bridges to a type U
unconditionally (e.g., Array
↔︎ NSArray
, or String
↔︎ NSString
), you can bridge between them with an as
cast: let a1: [Int] = [1, 2, 3]; let a2 = a1 as NSArray
, or let s1: NSString = "Hello"; let s2 = s1 as String
)T
bridges to a type U
conditionally (e.g., NSNumber
↔︎ Bool
above, since not all numbers can be converted to a Bool
), the same can be done with an as?
castOtherwise, bridging is a feature that's you can't meaningfully opt into outside of the compiler, standard library, and Apple frameworks. The Swift standard library has an underscored _ObjectiveCBridgeable
that Foundation uses to add bridging implementations (e.g., bridging Array
↔︎ NSArray
, or e.g., bridging UUID
↔︎ NSUUID
), but a lot of bridging mechanisms are also built into the compiler.
Upvotes: 3