Swift use of break in guard statement - swift

I'm trying to use break in an guard statement, but the compiler tells me
'break' is only allowed inside a loop, if, do, or switch
Is possible to write something like in this snippet (this is just an MCV)?
func test(string: String?, x: Int) {
print("Function Scope BEGIN")
if x > 4 {
guard let pr = string else { break }
print(pr)
}
else {
print("Not")
}
print("Function Scope END")
}

Yes it is possible. You can use unlabeled break statements inside loops, but not inside an if block. You can use labeled break statements though. For example, this version of your code will work:
func test(string: String?, x: Int) {
print("Function Scope BEGIN")
someLabel: if x > 4 {
guard let pr = string else { break someLabel }
print(pr)
}
else {
print("Not")
}
print("Function Scope END")
}

A break statement can only be used inside a guard let if the guard-let is inside a loop.
In your use-case, I'd say you should use an if-let instead, since the alternative option of return is not what is desired.
func test(string: String?, x: Int) {
print("Function Scope BEGIN")
if x > 4 {
if let pr = string { print(pr) }
}
else {
print("Not")
}
print("Function Scope END")
}

Related

Swift: How to deallocate properties when init throw?

There is a memory leak with this example code.
The pointer1 and pointer2 allocate before Person inits successfully. If the init function throws an Error. The deinit function will never be executed. So the pointer1 and pointer2 will never be released.
import XCTest
class Person {
// case1
let pointer1: UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>.allocate(capacity: 1)
// case2
let pointer2: UnsafeMutablePointer<Int>
let name: String
init(name: String) throws {
// case2
self.pointer2 = UnsafeMutablePointer<Int>.allocate(capacity: 1)
if name == "UnsupportName" {
throw NSError()
}
self.name = name
}
deinit {
pointer1.deallocate()
pointer2.deallocate()
}
}
class InterestTests: XCTestCase {
func testExample() {
while true {
_ = try? Person(name: "UnsupportName")
}
}
}
Sometimes the logic is very complicated. In my real cases. There are a lot of allocate and throws with if and guard. Some it's hard to control it.
Is there any way to avoid this memory leak?
Here is a similar question: https://forums.swift.org/t/deinit-and-failable-initializers/1199
In your specific example, the solution is straightforward. Don't allocate any memory until you have resolved all possible failures:
class Person {
let aPointer: UnsafeMutablePointer<Int> // Do not allocate here.
let name: String
init(name: String) throws {
// Validate everything here
guard name != "UnsupportName" else {
throw NSError()
}
// After this point, no more throwing:
self.name = name
// Move the allocation here
self.aPointer = UnsafeMutablePointer.allocate(capacity: 1)
}
deinit {
aPointer.deallocate()
}
}
But the more general solution is to use do/catch like anywhere else you need to manage errors:
class Person {
let aPointer = UnsafeMutablePointer<Int>.allocate(capacity: 1)
let name: String
init(name: String) throws {
do {
if name == "UnsupportName" {
throw NSError()
}
self.name = name
} catch let e {
self.aPointer.deallocate()
throw e
}
}
deinit {
aPointer.deallocate()
}
}
I would be tempted to move the .allocate inside the init, just to make it a bit more visible what's going on. The key point is that you should either allocate all your memory first, before anything can throw (so you know you can deallocate it all), or all after the last throw (so you know you don't have anything to deallocate).
Looking at the solution you've added, it's ok, but suggests dangerous logic surrounding it. It would be much better to unwind this to place the allocation into their own objects (which would almost certainly also get rid of the UnsafeMutablePointers; needing a lot of those in a single class is very suspicious).
That said, there are cleaner ways IMO to build the error handling along this path.
extension UnsafeMutablePointer {
static func allocate(capacity: Int, withCleanup cleanup: inout [() -> Void]) -> UnsafeMutablePointer<Pointee> {
let result = allocate(capacity: capacity)
result.addTo(cleanup: &cleanup)
return result
}
func addTo(cleanup: inout [() -> Void]) {
cleanup.append { self.deallocate() }
}
}
This lets UnsafeMutablePointers append cleanup information into an array, rather than creating a lot of defer blocks, which raises the risk of missing one during cleanup.
With that, your init looks like:
init(name: String) throws {
var errorCleanup: [() -> Void] = []
defer { for cleanup in errorCleanup { cleanup() } }
// deallocate helper for case1
pointer1.addTo(cleanup: &errorCleanup)
// case2
self.pointer2 = UnsafeMutablePointer<Int>.allocate(capacity: 1, withCleanup: &errorCleanup)
// case ...
if name == "UnsupportName" {
throw NSError()
}
self.name = name
// In the end. set deallocate helpers to nil
errorCleanup.removeAll()
}
Of course that setups the danger of calling allocate(capacity:) rather than allocate(capacity:withCleanup:). So you could fix that by wrapping it into another type; a reference type that automatically deallocates itself.
class SharedPointer<Pointee> {
let ptr: UnsafeMutablePointer<Pointee>
static func allocate(capacity: Int) -> SharedPointer {
return .init(pointer: UnsafeMutablePointer.allocate(capacity: capacity))
}
init(pointer: UnsafeMutablePointer<Pointee>) {
self.ptr = pointer
}
deinit {
ptr.deallocate()
}
}
With that, this becomes (no deinit required):
class Person {
// case1
let pointer1 = SharedPointer<Int>.allocate(capacity: 1)
// case2
let pointer2: SharedPointer<Int>
let name: String
init(name: String) throws {
// case2
self.pointer2 = SharedPointer<Int>.allocate(capacity: 1)
if name == "UnsupportName" {
throw NSError()
}
self.name = name
}
}
You'd probably want to write various helpers for dealing with .ptr.
Of course this might lead you to build specific versions of SharedPointer to deal with each kind of thing (like "father" rather than "int"). If you continue down that road, you'll find the UnsafeMutablePointers evaporate, and the problem goes away. But you don't have to go that far, and SharedPointer will do the work for you.
I found a solution to my problem.
import XCTest
class Person {
// case1
let pointer1: UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>.allocate(capacity: 1)
// case2
let pointer2: UnsafeMutablePointer<Int>
let name: String
init(name: String) throws {
// deallocate helper for case1
var deallocateHelper1: UnsafeMutablePointer<Int>? = self.pointer1
defer {
deallocateHelper1?.deallocate()
}
// case2
self.pointer2 = UnsafeMutablePointer<Int>.allocate(capacity: 1)
var deallocateHelper2: UnsafeMutablePointer<Int>? = self.pointer2
defer {
deallocateHelper2?.deallocate()
}
// case ...
if name == "UnsupportName" {
throw NSError()
}
self.name = name
// In the end. set deallocate helpers to nil
deallocateHelper1 = nil
deallocateHelper2 = nil
}
deinit {
pointer1.deallocate()
pointer2.deallocate()
}
}
class InterestTests: XCTestCase {
func testExample() {
while true {
_ = try? Person(name: "UnsupportName")
}
}
}
Another solution.
class Person {
let name: String
let pointer1: UnsafeMutablePointer<Int>
let pointer2: UnsafeMutablePointer<Int>
init(name: String) throws {
var pointers: [UnsafeMutablePointer<Int>] = []
do {
let pointer1 = UnsafeMutablePointer<Int>.allocate(capacity: 1)
pointers.append(pointer1)
let pointer2 = UnsafeMutablePointer<Int>.allocate(capacity: 1)
pointers.append(pointer2)
if name == "Unsupported Name" {
throw NSError()
}
self.pointer1 = pointer1
self.pointer2 = pointer2
self.name = name
} catch {
pointers.forEach { $0.deallocate() }
throw error
}
}
deinit {
pointer1.deallocate()
pointer2.deallocate()
}
}

How can I know that a completion block didn't executed but method finished?

How can I programmatically catch the case when completion does not execute?
I can not modify exists method, because this is a simple replacer for a Firebase observe.
func exists(completion: (_ a: Int) -> ()) {
//async call with a completion handler where I get the `a` value
if a % 2 == 0 {
completion(a)
}
..............//other cases
}
func check() {
exists { a in
print(a)
}
}
I thought of some flag, but how do I know that exists ended?
There's many ways of doing what you're trying to do, you could set flags (booleans), you could use optionals in the completion closure, you can use two closures...
I'm posting a way which I find the nicest, but it's purely subjective.
You could change the completion closure argument to be a Result enum for example.
enum Result {
case .success(Int)
case .failure
}
Then in the completion closure, you would replace the argument with this enum.
func exists(completion: (_ result: Result) -> ()) {
let a = arc4random()
if a % 2 == 0 {
completion(.success(a))
} else {
//other cases
completion(.failure)
}
}
func check() {
exists { result in
switch result {
case .succeess(let number):
print(number)
case .failure:
print("Finished without number")
}
}
}
Best way to achieve what you want is to use DisptachGroup.
func exists(completion: (_ a: Int) -> ()) {
completion(1)
}
let group = DispatchGroup()
group.enter()
exists { (a) in
group.leave()
}
group.notify(queue: .main) {
print("Did finish 'exists' function!")
}

Swift defer statement placement [duplicate]

I have the following swift code executing in playground:
func A() {
print ("Hello")
guard 1 == 2 else {
return
}
defer {
print ("World")
}
}
A()
I expected to see
Hello
World
Instead only the Hello is printed. Why is this? What am I missing?
Here is a better example:
enum MyError: ErrorType {
case TriggerDefer
}
func throwsMyError() throws {
let myzero = Int(arc4random_uniform(1))
guard myzero > 1 else {
throw MyError.TriggerDefer
}
}
func A() throws {
try throwsMyError()
defer {
print ("Hello World")
}
}
As per the answers and comments, the correct way to do this (with an example) is
enum MyError: ErrorType {
case TriggerDefer
}
func throwsMyError() throws {
let myzero = Int(arc4random_uniform(1))
print("Hello")
guard myzero > 1 else {
throw MyError.TriggerDefer
}
}
func A() throws {
defer {
print ("World")
}
try throwsMyError()
}
The output will now be
Hello
World
What you're missing is that deferis not magic. It is executable code, just like any other code. If the path of execution never encounters it, there is nothing to be deferred. This is why it should always be dead first in the block on whose exit it is to be executed — so that we guarantee that it is encountered.
Put the defer block before the scope is exited:
func A() {
print ("Hello")
defer {
print ("World")
}
guard 1 == 2 else {
return
}
}
A()

Why isn't guard let foo = foo valid?

In Swift, you can use if let optional binding to unwrap an optional into a constant or variable with the same name:
func test()
{
let a: Int? = 1
if let a = a {
print("a = \(a)")
}
}
For everything inside the if let statement, the optional a is unwrapped into a regular int.
Likewise, I can use a guard statement to achieve a similar effect
func test()
{
let a: Int? = 1
guard let requiredA = a else{
return
}
print("a = \(requiredA)")
}
However, I can't use code like this: guard let a = a else:
func test()
{
let a: Int? = 1
guard let a = a else{
return
}
print("a = \(a)")
}
Why not?
In a guard statement, if the conditional of the guard statement fails, the else clause is executed and you exit the current scope. If the conditional succeeds, a new variable/constant is created from guard statement's closing brace to the end of the current scope.
Why can't I do the same trick of mapping an optional into a variable/constant with the same name for remainder of the current scope?
P.S.: I realize this question isn't a perfect fit for this site. I'm open to suggestions as to where would be a better place for this question.
The reason you can't do this:
func test()
{
let a: Int? = 1
guard let a = a else{
return
}
print("a = \(a)")
}
is because guard creates the new variable in the same scope, thus you have two variables called a in the same scope. One is an Int and the other is an Int?. That is not allowed.
The error that you get Definition conflicts with previous value is exactly the same as if you had done this:
func test()
{
let a: Int? = 1
let a = a!
}
Compare that with:
func test()
{
let a: Int? = 1
if let a = a {
print("a = \(a)")
}
}
In this case, the new variable a which is an Int exists only in the new scope of the if's then clause, so this works.
From the comments:
But I submit to you that the section of code after the closing brace
and to the end of the enclosing scope is actually an inner scope.
I can understand that you would like it to be so, but it isn't. If that were the case, then you could do this, but it too gives an error:
func test()
{
let a: Int? = 1
guard let b = a else{
return
}
print("b = \(b)")
let a = 5 // Definition conflicts with previous value
print("a = \(a)")
}
The beauty of guard is that it doesn't create new scopes and you avoid creating the pyramid of death that results when you repeatedly use if let to unwrap optionals (and in the process create new scopes).
See the follow-up question
When did guard let foo = foo become legal? for more insight on this topic.

How do I tell which guard statement failed?

If I’ve got a bunch of chained guard let statements, how can I diagnose which condition failed, short of breaking apart my guard let into multiple statements?
Given this example:
guard let keypath = dictionary["field"] as? String,
let rule = dictionary["rule"] as? String,
let comparator = FormFieldDisplayRuleComparator(rawValue: rule),
let value = dictionary["value"]
else
{
return nil
}
How can I tell which of the 4 let statements was the one that failed and invoked the else block?
The simplest thing I can think of is to break out the statements into 4 sequential guard else statements, but that feels wrong.
guard let keypath = dictionary["field"] as? String
else
{
print("Keypath failed to load.")
self.init()
return nil
}
guard let rule = dictionary["rule"] as? String else
{
print("Rule failed to load.")
self.init()
return nil
}
guard let comparator = FormFieldDisplayRuleComparator(rawValue: rule) else
{
print("Comparator failed to load for rawValue: \(rule)")
self.init()
return nil
}
guard let value = dictionary["value"] else
{
print("Value failed to load.")
self.init()
return nil
}
If I wanted to keep them all in one guard statement, I can think of another option. Checking for nils inside the guard statement might work:
guard let keypath = dictionary["field"] as? String,
let rule = dictionary["rule"] as? String,
let comparator = FormFieldDisplayRuleComparator(rawValue: rule),
let value = dictionary["value"]
else
{
if let keypath = keypath {} else {
print("Keypath failed to load.")
}
// ... Repeat for each let...
return nil
}
I don't even know if that will compile, but then I might as well have used a bunch of if let statements or guards to begin with.
What's the idiomatic Swift way?
Erica Sadun just wrote a good blog post on this exact topic.
Her solution was to hi-jack the where clause and use it to keep track of which guard statements pass. Each successful guard condition using the diagnose method will print the file name and the line number to the console. The guard condition following the last diagnose print statement is the one that failed. The solution looked like this:
func diagnose(file: String = #file, line: Int = #line) -> Bool {
print("Testing \(file):\(line)")
return true
}
// ...
let dictionary: [String : AnyObject] = [
"one" : "one"
"two" : "two"
"three" : 3
]
guard
// This line will print the file and line number
let one = dictionary["one"] as? String where diagnose(),
// This line will print the file and line number
let two = dictionary["two"] as? String where diagnose(),
// This line will NOT be printed. So it is the one that failed.
let three = dictionary["three"] as? String where diagnose()
else {
// ...
}
Erica's write-up on this topic can be found here
Normally, a guard statement doesn't let you distinguish which of its conditions wasn't satisfied. Its purpose is that when the program executes past the guard statement, you know all the variables are non-nil. But it doesn't provide any values inside the guard/else body (you just know that the conditions weren't all satisfied).
That said, if all you want to do is print something when one of the steps returns nil, you could make use of the coalescing operator ?? to perform an extra action.
Make a generic function that prints a message and returns nil:
/// Prints a message and returns `nil`. Use this with `??`, e.g.:
///
/// guard let x = optionalValue ?? printAndFail("missing x") else {
/// // ...
/// }
func printAndFail<T>(message: String) -> T? {
print(message)
return nil
}
Then use this function as a "fallback" for each case. Since the ?? operator employs short-circuit evaluation, the right-hand side won't be executed unless the left-hand side has already returned nil.
guard
let keypath = dictionary["field"] as? String ?? printAndFail("missing keypath"),
let rule = dictionary["rule"] as? String ?? printAndFail("missing rule"),
let comparator = FormFieldDisplayRuleComparator(rawValue: rule) ?? printAndFail("missing comparator"),
let value = dictionary["value"] ?? printAndFail("missing value")
else
{
// ...
return
}
Very good question
I wish I had a good answer for that but I have not.
Let's begin
However let's take a look at the problem together. This is a simplified version of your function
func foo(dictionary:[String:AnyObject]) -> AnyObject? {
guard let
a = dictionary["a"] as? String,
b = dictionary[a] as? String,
c = dictionary[b] else {
return nil // I want to know more ☹️ !!
}
return c
}
Inside the else we don't know what did go wrong
First of all inside the else block we do NOT have access to the constants defined in the guard statement. This because the compiler doesn't know which one of the clauses did fail. So it does assume the worst case scenario where the first clause did fail.
Conclusion: we cannot write a "simple" check inside the else statement to understand what did not work.
Writing a complex check inside the else
Of course we could replicate inside the else the logic we put insito the guard statement to find out the clause which did fail but this boilerplate code is very ugly and not easy to maintain.
Beyond nil: throwing errors
So yes, we need to split the guard statement. However if we want a more detailed information about what did go wrong our foo function should no longer return a nil value to signal an error, it should throw an error instead.
So
enum AppError: ErrorType {
case MissingValueForKey(String)
}
func foo(dictionary:[String:AnyObject]) throws -> AnyObject {
guard let a = dictionary["a"] as? String else { throw AppError.MissingValueForKey("a") }
guard let b = dictionary[a] as? String else { throw AppError.MissingValueForKey(a) }
guard let c = dictionary[b] else { throw AppError.MissingValueForKey(b) }
return c
}
I am curious about what the community thinks about this.
One possible (non-idiomatic) workaround: make use of the where clause to track the success of each subsequent optional binding in the guard block
I see nothing wrong with splitting up your guard statements in separate guard blocks, in case you're interested in which guard statement that fails.
Out of a technical perspective, however, one alternative to separate guard blocks is to make use of a where clause (to each optional binding) to increment a counter each time an optional binding is successful. In case a binding fails, the value of the counter can be used to track for which binding this was. E.g.:
func foo(a: Int?, _ b: Int?) {
var i: Int = 1
guard let a = a where (i+=1) is (),
let b = b where (i+=1) is () else {
print("Failed at condition #\(i)")
return
}
}
foo(nil,1) // Failed at condition #1
foo(1,nil) // Failed at condition #2
Above we make use of the fact that the result of an assignment is the empty tuple (), whereas the side effect is the assignment to the lhs of the expression.
If you'd like to avoid introducing the mutable counter i prior the scope of guard clause, you could place the counter and the incrementing of it as a static class member, e.g.
class Foo {
static var i: Int = 1
static func reset() -> Bool { i = 1; return true }
static func success() -> Bool { i += 1; return true }
}
func foo(a: Int?, _ b: Int?) {
guard Foo.reset(),
let a = a where Foo.success(),
let b = b where Foo.success() else {
print("Failed at condition #\(Foo.i)")
return
}
}
foo(nil,1) // Failed at condition #1
foo(1,nil) // Failed at condition #2
Possibly a more natural approach is to propagate the value of the counter by letting the function throw an error:
class Foo { /* as above */ }
enum Bar: ErrorType {
case Baz(Int)
}
func foo(a: Int?, _ b: Int?) throws {
guard Foo.reset(),
let a = a where Foo.success(),
let b = b where Foo.success() else {
throw Bar.Baz(Foo.i)
}
// ...
}
do {
try foo(nil,1) // Baz error: failed at condition #1
// try foo(1,nil) // Baz error: failed at condition #2
} catch Bar.Baz(let num) {
print("Baz error: failed at condition #\(num)")
}
I should probably point out, however, that the above is probably closer to be categorized as a "hacky" construct, rather than an idiomatic one.
The simplest thing I can think of is to break out the statements into 4 sequential guard else statements, but that feels wrong.
In my personal opinion, the Swift way shouldn't require you to check whether the values are nil or not.
However, you could extend Optional to suit your needs:
extension Optional
{
public func testingForNil<T>(#noescape f: (Void -> T)) -> Optional
{
if self == nil
{
f()
}
return self
}
}
Allowing for:
guard let keypath = (dictionary["field"] as? String).testingForNil({ /* or else */ }),
let rule = (dictionary["rule"] as? String).testingForNil({ /* or else */ }),
let comparator = FormFieldDisplayRuleComparator(rawValue: rule).testingForNil({ /* or else */ }),
let value = dictionary["value"].testingForNil({ /* or else */ })
else
{
return nil
}
My two cents:
Since Swift doesn't let me add the where in the guard let, I came up with this solution instead:
func validate<T>(_ input: T?, file: String = #file, line: Int = #line) -> T? {
guard let input = input else {
print("Nil argument at \(file), line: \(line)")
return nil
}
return input
}
class Model {
let id: Int
let name: String
init?(id: Int?, name: String?) {
guard let id = validate(id),
let name = validate(name) else {
return nil
}
self.id = id
self.name = name
}
}
let t = Model(id: 0, name: "ok") // Not nil
let t2 = Model(id: 0, name: nil) // Nil
let t3 = Model(id: nil, name: "ok") // Nil
I think other answers here are better, but another approach is to define functions like this:
func checkAll<T1, T2, T3>(clauses: (T1?, T2?, T3?)) -> (T1, T2, T3)? {
guard let one = clauses.0 else {
print("1st clause is nil")
return nil
}
guard let two = clauses.1 else {
print("2nd clause is nil")
return nil
}
guard let three = clauses.2 else {
print("3rd clause is nil")
return nil
}
return (one, two, three)
}
And then use it like this
let a: Int? = 0
let b: Int? = nil
let c: Int? = 3
guard let (d, e, f) = checkAll((a, b, c)) else {
fatalError()
}
print("a: \(d)")
print("b: \(e)")
print("c: \(f)")
You could extend it to print the file & line number of the guard statement like other answers.
On the plus side, there isn't too much clutter at the call site, and you only get output for the failing cases. But since it uses tuples and you can't write a function that operates on arbitrary tuples, you would have to define a similar method for one parameter, two parameters etc up to some arity. It also breaks the visual relation between the clause and the variable it's being bound to, especially if the unwrapped clauses are long.
This code can be used for all guard and if logic tests like optional, bool and case tests. It prints a line of a logic test which failed.
class GuardLogger {
var lastGoodLine: Int
var lineWithError: Int { lastGoodLine + 1 }
var file: String
var function: String
init(file: String = #file, function: String = #function, line: Int = #line) {
self.lastGoodLine = line
self.file = file
self.function = function
}
func log(line: Int = #line) -> Bool {
lastGoodLine = line
return true
}
func print() {
Swift.print([file, function, String(lineWithError)].joined(separator: " "))
}
}
let testBoolTrue = true
let testBoolFalse = false
let guardLogger = GuardLogger()
guard
testBoolTrue, guardLogger.log(),
let testOptionalBoolTrue = Optional(testBoolTrue), guardLogger.log(),
let selfIsViewController = self as? UIViewController, guardLogger.log(),
testBoolTrue == false, guardLogger.log() // this fails
else {
print(guardLogger.lastGoodLine)
fatalError()
}