mikeash.com: just this guy, you know?

Posted at 2015-06-19 13:12 | RSS feed (Full text feed) | Blog Index
Next article: Friday Q&A 2015-07-03: Address Sanitizer
Previous article: I Do Not Agree To Your Terms
Tags: fridayqna swift
Friday Q&A 2015-06-19: The Best of What's New in Swift
by Mike Ash  

Apple made a lot of interesting announcements at WWDC this year about the release of Swift 2 and the new features in it, in among various other announcements of little interest. In addition to the announcement that Swift will be made open source, which is a huge deal all by itself, Swift 2 contains a lot of great new features which significantly improve the language. Today I'm going to talk about the most important ones, what they are, and why they're useful.

Function Pointers
This is my favorite new Swift feature by far. It's a relatively small but important feature that closes the last big hole remaining in Swift's bridging to C.

Previously, Swift treated C function pointer types as opaque types. If something returned a function pointer, you could obtain the value. If something accepted a function pointer as a parameter, you could pass that value. But there was no way to call a function pointer, and more importantly no way to create one that referred to your Swift code. You got a CFunctionPointer value that you could use for almost nothing. The only reasonable way to interact with these APIs was to write C or Objective-C APIs that wrapped them.

The terrible world of CFunctionPointer is gone in Swift 2. Instead, Swift function types have trifurcated into variants. Any Swift function type can optionally be annotated with a @convention specifier which says what kind of function it is. The swift convention is the default and indicates a normal Swift function. The block convention indicates an Objective-C block type. These were automatically bridged, and still are, but now the typing is more explicit. Finally, the c convention indicates a C function pointer. Function types annotated with @convention(c) still behave mostly normally, so you can call them and pass them as usual.

I said "mostly" above because C function pointers have significant limitations which are necessarily reflected in Swift. Specifically, C function pointers are pure pointers to code, with no associated data. Swift functions often have implicit associated data, such as methods in an object, or closures which capture values from the enclosing scope. Because of this mismatch, only global Swift functions or nested or anonymous functions with no captured data can be passed as @convention(c) parameters.

Here's an example of this new feature at work:

    atexit({ print("Goodbye!") })

This will print "Goodbye!" when the program exits.

To illustrate the limitations, this code will not work (unless placed at the global scope):

    let x = 42
    atexit({ print("The answer is \(x)") })

The compiler complains: "A C function pointer cannot be formed from a closure that captures context."

This limitation is only to be expected, and is what we've always had to deal with in C anyway. Despite that, this is still extremely useful and lets us use a common class of C APIs which were basically off limits from Swift before.

Protocol Extensions
This is my favorite new Swift feature by far. Protocol extensions allow protocols to contain method implementations, not just method declarations. This is a feature I've wanted in Objective-C for ages, and I'm glad to see it show up even if it took a new language.

Previously, in both Objective-C and Swift, protocols contained only method declarations. They were pure interface definitions, just a list of things for conforming types to implement. With protocol extensions in Swift 2, protocols can now contain method implementations as well as declarations.

Often there's some functionality that can apply to all types that conform to a certain interface. For example, all collections can support the concept of mapping over the collection to create a new one. With the old style of protocols, there were two ways to give all collections this capability. You could either put the method in the protocol and require each conforming type to implement it, or you could write a global function that works on values of the protocol type.

Cocoa mostly went with the former solution. Although they're not formally part of any protocol, the various Cocoa collections tend to have similar functionality such as enumerateObjectsUsingBlock: which each one implements separately.

Swift previously went with the latter solution. Global functions like map operated on any CollectionType. This allows for a nice shared implementation, but awkward syntax and no ability to override the implementation to specialize it for a particular type.

With protocol extensions, there's a third option that's far superior. map can be implemented in an extension on the CollectionType protocol. All types which conform to CollectionType then automatically get an implementation of a map method for free.

As an example, here's a simple reimplementation of `map':

    extension CollectionType {
        func myMap<U>(f: Self.Generator.Element -> U) -> [U] {
            var result: [U] = []
            for elt in self {
                result.append(f(elt))
            }
            return result
        }
    }

    [1, 2, 3, 4].myMap({ $0 * 2 })
    // This produces [2, 4, 6, 8]

This previously could have been done as an extension on Array, but then would only be available on Array. With a protocol extension, it also applies to Set and ArraySlice and every other implementation of CollectionType without any additional effort.

One really interesting feature of Swift protocol extensions is that they can be given type constraints. For example, you might want to implement a max property. But max doesn't conceptually work with all collections, only collections of objects that have some sort of ordering. That's not a problem! Just add a requirement to the extension that the collection elements must be Comparable:

    extension CollectionType where Self.Generator.Element: Comparable {
        var max: Self.Generator.Element {
            var best = self[self.startIndex]
            for elt in self {
                if elt > best {
                    best = elt
                }
            }
            return best
        }
    }

    Set([5, 4, 3, 2, 1]).max
    // This produces 5

    [NSObject(), NSObject()].max
    // This produces an error, as NSObject is not Comparable

There's one wrinkle with protocol extensions, which is a subtle but important distinction which determines whether protocol extension methods are subject to dynamic dispatch.

A method in a protocol extension may also be declared in the protocol itself, or it may exist solely in the extension. Methods which exist solely in the extension are not dynamically dispatched and cannot be overridden. Methods which are also declared in the protocol itself are dynamically dispatched and can be overridden. This is a bit difficult to explain, here's an example:

    protocol P {
        func a()
    }

    extension P {
        func a() {
            print("default implementation of A")
        }

        func b() {
            print("default implementation of B")
        }
    }

    struct S: P {
        func a() {
            print("specialized implementation of A")
        }

        func b() {
            print("specialized implementation of B")
        }
    }

    let p: P = S()
    p.a()
    p.b()

This will print "specialized implementation of A" followed by "default implementation of B." Although the struct contains an implementation of b, it does not override the protocol's b, because the main protocol doesn't contain a declaration of b. The distinction is ultimately between methods in the protocol which happen to have a default implementation, and method implementations that are attached to the protocol.

This is a bit of a weird spot with protocol extensions and I'm hoping that Apple will improve upon this somehow as the beta cycle proceeds. As it stands, it's not a big problem, just something to be aware of.

(My brief and modest proposal for a fix: borrow the final keyword to indicate "no dynamic dispatch" on protocol extension messages.)

I believe protocol extensions may also be Apple's answer to the question of optional protocol methods. Pure Swift protocols couldn't, and still can't, contain optional methods. We're used to having optional methods in Objective-C protocols for things like delegates:

    @protocol MyClassDelegate
    @optional

    - (BOOL)shouldDoThingOne;
    - (BOOL)shouldDoThingTwo

    @end

The users of the protocol would then check respondsToSelector: before invoking these methods. Pure Swift has no equivalent:

    protocol MyClassDelegate {
        func shouldDoThingOne() -> Bool
        func shouldDoThingTwo() -> Bool
    }

Previously, anything that implemented this protocol was required to implement these methods. This conflicts with Cocoa's notion of a delegate as being able to provide optional customizations, with sensible default behaviors. With protocol extensions in Swift 2, the sensible default behaviors can now be provided in the protocol itself:

    extension MyClassDelegate {
        func shouldDoThingOne() -> Bool {
            // Thing one is harmless and should almost always be done
            return true
        }

        func shouldDoThingTwo() -> Bool {
            // Thing two is a menace and should not be done without careful consideration
            return false
        }
    }

This provides the same ultimate functionality as @optional in Objective-C, but without requiring runtime checks.

Error Handling
This is my favorite new Swift feature by far. Many people despaired when Swift showed up with no support for exceptions. Yet this despair was somewhat abstract, since Objective-C doesn't really support exceptions either. The syntax, compiler, and runtime support for exceptions is there, but writing code that can tolerate exceptions being thrown through it is hard, and most people don't do it. ARC drove this point home when it arrived, as ARC's own generated code isn't exception safe by default.

Because of this state of affairs, Objective-C de facto only uses exceptions to signal programming errors. There are a handful of, uh, exceptions to this, but Cocoa code generally uses exceptions only to signal assertion failures. Third-party code typically follows along. Which is kind of weird, since the whole idea with exceptions is that you can catch them and continue execution, but doing that after an assertion failure is a really, really bad idea.

Since exceptions can't really be used to signal recoverable errors in Objective-C in practice, Cocoa adopted the NSError ** convention, where any method that can signal an error takes an extra NSError ** parameter and uses that to return information about the failure. This works, but it turns the code into a terrible slog because the NSError ** interface just isn't very nice.

It also encourages ignoring errors, since by convention these parameters accept NULL to mean "I don't care about the error, don't give it to me." I can't tell you how many times I've seen fellow programmers scratching their head over why some piece of code won't work, and they're passing NULL for the error parameter. The computer is trying to tell you what's going wrong, if you'd just listen!

Swift 2 introduces an error handling facility that attempts to find a middle ground between the two techniques. Syntactically, it resembles exceptions. Semantically, it's more like NSError **. The result is unusual but looks pretty good.

Let's look at an example. Writing a file to disk is a common operation that can fail. In Cocoa, it looks like this:

    NSError *error;
    BOOL success = [data writeToFile: path options: options error: &error];
    if(!success) {
        // respond to the error somehow
        return;
    }
    // proceed normally

In a world where Cocoa used exceptions to signal these kinds of errors, this code would look something like:

    @try {
        [data writeToFile: path options: options];
        // proceed normally
    } @catch(NSError *error) {
        // respond to the error somehow
    }

In Swift 2, you write:

    do {
        try data.writeToFile(path, options: options)
        // proceed normally
    } catch {
        // respond to the error somehow
    }

This is superficially similar to the code with exceptions, but there are some important differences in Swift's approach.

Swift errors are checked by the compiler. This is similar to languages like Java where the possibility of throwing exceptions is part of a method's type signature. It's radically different from Objective-C and a lot of other languages where anything and everything can potentially throw an exception.

When exceptions are completely unchecked, that means that either every bit of code has to be written with the possibility that an exception will be thrown through it, or you have to know from other sources (documentation, reading the source code) which methods can throw and which can't. With the Swift approach, if you forget that writeToFile throws, the compiler will tell you. With the Objective-C approach, your code will compile and run and work just fine until the write fails someday, at which point you suddenly find yourself working as a 19th century Cockney bootblack, pondering the meaning of "undefined behavior" in between shining Victorians' shoes.

One feature of Swift error handling that is unique (as far as I know) is that the try keyword is required for each statement that can throw. With Java-style checked exceptions, the only requirement is that a throwing method either exist within a method that can throw the appropriate type, or within a suitable try block. To illustrate the difference, consider these two pieces of hypothetical code:

    // Java style
    try {
        String name = textField.value();
        Data nameBytes = name.getBytes("UTF-8");
        nameBytes.writeToFile(path);
        proceedWithName(name);
    } catch(IOException exception) {
        ...
    }

    // Swift style
    do {
        let name = textField.value
        let nameBytes = name.dataUsingEncoding(NSUTF8StringEncoding)!
        try nameBytes.writeToFile(path, options: [])
        proceedWithName(name)
    } catch {
        ...
    }

Quick, which calls in the first version can throw? There's no way to know unless you know (or look up) the interface for every call used in the try block. Now, which calls in the second version can throw? That's easy: the writeToFile call.

You might say that it's pretty obvious what throws just from looking at the calls. But maybe proceedWithName can also produce an error and throw. Compare:

    // Java style
    try {
        String name = textField.value();
        Data nameBytes = name.getBytes("UTF-8");
        nameBytes.writeToFile(path);
        proceedWithName(name);
    } catch(IOException exception) {
        ...
    }

    // Swift style
    do {
        let name = textField.value
        let nameBytes = name.dataUsingEncoding(NSUTF8StringEncoding)!
        try nameBytes.writeToFile(path, options: [])
        try proceedWithName(name)
    } catch {
        ...
    }

The Java version doesn't change, while the Swift one shows that there are now two calls that can potentially fail.

In Java, you'd typically want to put as few things into the try block as possible, just to make it obvious which bits can throw and which bits are incidental. In Swift, you don't have to worry about it, since each throwing statement is clearly marked.

This becomes even more significant when an entire method is marked as throws. With Java-style checked exceptions, once you've declared that some method throws IOException then potentially anything within it could throw that type. With Swift, a method marked as throws still needs to annotate each potentially-throwing call with try, so it's still obvious.

Another nice difference from Java is a built-in "really, this can't fail" mechanism, in the form of try! Sometimes there's a method that can fail only in some circumstances, and you know that it can't fail the way you're using it. The getBytes call above is a good example of this in Java: it throws UnsupportedEncodingException but it's guaranteed never to throw with UTF-8. The call needs a dummy try wrapper even though you know it can't fail. In Swift, you can use try! to accomplish this, which is both clearer and shorter. This fits nicely with the ! suffix for unwrapping optionals that you know cannot be nil, as used with dataUsingEncoding above, and the as! operator for downcasts you know will succeed.

An interesting contrast with Java is that while Swift errors are checked, only the fact that an error can be thrown is checked. When a Java method is annotated with throws, you specify the types that can be thrown. The caller then knows that it only needs to check for those types, and the compiler typechecks everything accordingly. Swift only annotates throws with no type information. This type information is useful in Java but can also be annoyingly restrictive when nesting throws calls. It will be interesting to see how it works out in Swift.

Guard Statements
This is my favorite new Swift feature by far. It's small and simple nearly to the point of being redundant, but it can make code much nicer to read and write.

The guard statement is essentialy an inverse if statement. In an if statement you write:

    if condition {
        // true branch
    } else {
        // false branch
    }

With guard, the true branch pops out to the upper level after the false branch:

    guard condition else {
        // false branch
    }
    // true branch

Note that the false branch must terminate execution within the enclosing scope somehow, such as by returning a value or throwing an error. You're guaranteed that the code in the true branch only executes in the case where the condition is true.

This makes guard a natural way to check non-fatal preconditions without running into the "pyramid of doom" that comes with multiple nested if statements, and without inverting conditions. For example, here's a typical pyramid:

    let fd1 = open(...)
    if fd1 >= 0 {
        let fd2 = open(...)
        if fd2 >= 0 {
            // use fd1 and fd2 here
            close(fd2)
        } else {
            // handle fd2 error
        }
        close(fd1)
    } else {
        // handle fd1 error
    }

This is pretty ugly, and as you stack them up it just gets worse. The primary code gets indented a lot and the error handling code gets exiled to a distant land far from the origin of the failure it's supposed to handle. We can avoid that by inverting the conditions:

    let fd1 = open(...)
    if fd1 == -1 {
        // handle fd1 error
        return
    }

    let fd2 = open(...)
    if fd2 == -1 {
        // handle fd2 error
        close(fd1)
        return
    }

    // use fd1 and fd2 here
    close(fd1)
    close(fd2)

This is decent, but it's a bit annoying that the conditions are now flipped to check for the error case rather than the good case. Worse is the fact that if you forget a return statement, the compiler doesn't care and your code will happily continue executing after the error case. guard solves both of these problems:

    let fd1 = open(...)
    guard fd1 >= 0 else {
        // handle fd1 error
        return
    }

    let fd2 = open(...)
    guard fd2 >= 0 else {
        // handle fd2 error
        close(fd1)
        return
    }

    // use fd1 and fd2 here
    close(fd1)
    close(fd2)

Better! This reads more clearly and I get more help from the compiler. But it's not that special, so why is it my favorite? It's because, like an if statement, guard statements can contain variable declarations and check for nil. Unlike if statements, the declared variables are available in the scope that contains the guard statement, not just within the statement's own scope. To illustrate, let's look an an optionals version of the above example, first with the pyramid of doom:

    if let file1 = Open(...) {
        if let file2 = Open(...) {
            // use file1 and file2
            file2.close()
        } else {
            // handle file2 error
        }
        file1.close()
    } else {
        // handle file1 error
    }

Let's invert the conditionals like before:

    if !let file1 =

Oops! You can't invert a let. Let's try again:

    let file1 = Open(...)
    if file1 == nil {
        // handle file1 error
        return
    }

    let file2 = Open(...)
    if file2 == nil {
        // handle file2 error
        file1.close()

Oops! The optional isn't unwrapped anymore. We'll have to do that separately. Let's try again:

    let file1Optional = Open(...)
    if file1Optional == nil {
        // handle file1 error
        return
    }
    let file1 = file1Optional!

    let file2Optional = Open(...)
    if file2Optional == nil {
        // handle file2 error
        file1.close()
        return
    }
    let file2 = file2Optional!

    // use file1 and file2 here
    file1.close()
    file2.close()

Got it. That's an unpleasant mess, though. guard makes it nice:

    guard let file1 = Open(...) else {
        // handle file1 error
        return
    }
    guard let file2 = Open(...) else {
        // handle file2 error
        file1.close()
        return
    }

    // use file1 and file2 here
    file1.close()
    file2.close()

Much better! The only really annoying thing here is the repetition of file1.close() and the fact that the cleanup code is so far away from the initialization code, which brings us to....

Defer Statements
This is my favorite new Swift feature by far. The defer statement is much like the finally statement in many other languages, except it doesn't have to be bundled with a try statement, and you can put it just about anywhere you want. You write defer { ... } and the code in that block will be executed when control leaves the enclosing scope, whether it left by running off the end, or hitting a return statement, or throwing an error.

This lets you put cleanup code next to the stuff it cleans up, instead of at the end. For example:

    let tmpMemory = malloc(...)
    defer { free(tmpMemory) }

    // use tmpMemory here

It goes well with guard so that creation, error handling, and cleanup can all live together. Here's the above example using defer:

    guard let file1 = Open(...) else {
        // handle file1 error
        return
    }
    defer { file1.close() }

    guard let file2 = Open(...) else {
        // handle file2 error
        return
    }
    defer { file2.close() }

    // use file1 and file2 here
    // no need for cleanup at the end, it's already done

Note that the defer for file1 handles both the normal case and the case where file2 failed. This eliminates the repetition from the previous example, and also helps ensure we don't forget cleanup on any branch. Since error handling code is often untested, failing to clean up in the event of an error can be a common problem, and defer ensures that can't happen.

Conclusion
This is my favorite article about new Swift features by far. Swift 2 looks like a great upgrade to Swift, addressing a lot of deficiencies and adding some great improvements.

That's it for today. Come back next time for more new and exciting stuff. Friday Q&A is driven by reader ideas, so if you have something you'd like to see covered, please send it in!

Did you enjoy this article? I'm selling a whole book full of them. It's available for iBooks and Kindle, plus a direct download in PDF and ePub format. It's also available in paper for the old-fashioned. Click here for more information.

Comments:

Fabio Ritrovato at 2015-06-19 13:44:27:
Methods which exsit solely in the extension are not dynamically dispatched and cannot be overridden.


This is not true, you can override method in the extension, but the one that will be called will be inferred from the variable type.
Taking out the cast to P in the example will print "specialized implementation of B"

I think this means that final can't really be used here, unless you actually want it to be final...

mikeash at 2015-06-19 14:01:56:
It's definitely not dynamically dispatched, otherwise the static type wouldn't influence which one gets called. And thus it's really just shadowed, not overridden.

Masklinn at 2015-06-19 14:49:44:
Am I smoking or are all 5 features your "favorite new Swift feature by far."?

mikeash at 2015-06-19 14:55:40:
I can neither confirm nor deny which features are my favorite.

Andrew at 2015-06-19 15:21:40:
When they say Swift 2 is going to be cross platform, what does that mean? Will I be able to write a full GUI and have it run on Mac OS and Linux? Or is it just the language, without Cocoa (or whatever Swift's equivalent is)?

mikeash at 2015-06-19 15:27:19:
My understanding is that Apple is going to release a Linux version of the compiler and the standard library. Whether "the standard library" includes anything from Foundation or whether it's just the Swift module is unknown. It's pretty much certain that nothing from AppKit will show up. If you want to write a GUI app in Swift that runs on Linux then you'll need to use some Linux GUI framework. The fact that Swift can talk to C almost seamlessly should make that pretty straightforward, at least.

Dan Smith at 2015-06-19 15:52:16:
The "@testable" attribute is sad that he didn't make an appearance in this article...

Granted, maybe that's just because I get annoyed when I see this type of comment in my code:

// Public only for testing

Arseny Kapoulkine at 2015-06-19 16:25:51:
> One feature of Swift error handling that is unique (as far as I know) is that the try keyword is required for each statement that can throw.

I'm not familiar with Swift but this feature looks like it works exactly as Rust try! macro (which desugars into returning a Result value with an error if the expression returned an error).

And geez, this "guard else" syntax is... weird. They could've taken 'unless' from Perl/Ruby (unless let would have worked as well...)

Jon Gary at 2015-06-19 16:33:14:
This is my favorite blog article by far.

mikeash at 2015-06-19 16:47:18:
Arseny Kapoulkine: It sounds like that's not quite the same, if it desugars into something. What's interesting about Swift's version is that not only does try apply to a single statement, but it's required, so you can be certain that any potentially throwing call will be indicated with a "try." Similar, though.

Keith Truch at 2015-06-19 17:06:21:
@Jon Gary: yours is my favorite comment by far

Jnosh at 2015-06-19 17:39:29:
borrow the final keyword to indicate "no static dispatch"

This was probably supposed to be be "static dispatch"?

Also, this is my favorite post on the citadel!

mikeash at 2015-06-19 17:42:02:
I think I meant "no dynamic dispatch," but, yes. I fixed it now, thanks!

Scott Gould at 2015-06-19 17:50:44:
"at which point you suddenly find yourself working as a 19th century Cockney bootblack, pondering the meaning of "undefined behavior" in between shining Victorians' shoes.”

This made my Friday. Thanks for another awesome post!

Swift is getting to look pretty close to complete. The one area that seems to be missing is some language constructs for concurrency. If you were Chris Lattner, how would you approach adding special concurrency syntax to Swift?

Is there anything else you’d add to Swift?

Jnosh at 2015-06-19 18:14:34:
Do you think "final" on protocol extensions should be mandatory or should the current behavior continue to exist (perhaps explicitly marked as "static")?

I kinda like your suggestion but it seems to have interesting implications:
- Potentially only place in the language with mandatory keywords for the default behavior.
- Two unrelated protocol extensions could coexist that contain the "same" method until a type conforms to both extensions at which point, if either of them is final, this would produce an error in the type that is extended by them.
Although this is related to collisions from protocol extensions anyway, which seems to be an open question for now how Swift is ultimately going to handle...

Tim Buchheim at 2015-06-19 19:33:42:
"It's a relatively small but important feature that closes the last big hole remaining in Swift's bridging to C."

Function pointers were certainly the biggest hole in Swift 1.x's C interoperability. And the solution they came up with is about as elegant as I could imagine. (I honestly wasn't expecting them to come up with such a good solution.)

Now the biggest remaining hole is Swift's handling of unions. Or non-handling of them. Currently any C union "foo" comes into Swift as "struct foo {}" … meaning you can't do anything with them, just like function pointers in Swift 1.x.

Whiel it feels like they could be imported as a kind of enum, that wouldn't actually work. I can't think of any good way to deal with them, and clearly the Swift team hasn't either.

Oh and I guess C bitfields are currently ignored too. Those seem conceptually easier to deal with but would still be ugly in Swift.

Rick at 2015-06-19 19:54:34:
What, #available isn't (also) by far your favorite new feature? It composes so nicely with guard clauses.

@Andrew + mikeash: When Apple talks about the Swift standard library going open source, they probably mean the Swift Standard Library (https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/SwiftStandardLibraryReference), without which Swift the language is pretty crippled. Not the rest of Cocoa or Cocoa Touch. (Though much of Foundation is already open-source, via CoreFoundation which is part of Darwin.)

@Jnosh: according to The Swift Programming Language, the most-constrained protocol extension wins naming conflicts. If you have a conflict between equally-constrained extensions, it's a compile error. (Want a way to disambiguate at the call site? Sounds like a good bug to file.)

mikeash at 2015-06-19 21:07:28:
Scott Gould: I'm not sure what Swift needs for concurrency. I think it's pretty good right now with GCD for higher-level stuff and pthreads or Cocoa threading APIs for lower-level stuff. We can probably do with just that for a good long while. It would be nice to have some atomic operations built in, especially if we could somehow get support for an atomic compare-and-swap operation on references.

Jnosh: I think both kinds of behavior make sense. Dynamic dispatch is usually preferable, but modifying the original protocol isn't always practical. If I'm adding an extension to, say, CollectionType, it makes sense to me that this won't be dynamically dispatched since that would require screwing around with the vtable for the original protocol which lives in the standard library. If it's my own protocol, though, then I definitely want to be able to make dynamically dispatched protocol methods with default implementations. I think the current state of things is pretty decent, and just propose making it a requirement to add final to method implementations in the static case to make it clear that they don't get dynamic dispatch.

Tim Buchheim: C interop certainly isn't complete, but I think it's now good enough for almost all cases. The number of APIs that expose unions or bitfields to clients is pretty small. It certainly would be nice to have, but I think Swift could go without for a long time. I can't think of a good way to make unions work either. Maybe they'll come up with something that looks natural, like they did with function pointers. Bitfields sound like they ought to be easier, and in fact would go nicely with some sort of native Swift bitfield support so we can enjoy the benefits of packed bitfields in Swift code too.

Rick: #available is nice, but not a particularly big deal in my eyes. I don't mean to put it down, as it'll make dealing with older OS releases nicer, but it didn't stand out to me like these other things did.

Arseny Kapoulkine at 2015-06-20 02:23:33:
@mikeash: Well, desugaring is just an implementation detail. In Rust it's a library-level macro; in Swift it's a language feature.

Regarding the necessity of try! - I believe in Rust Result type is tagged with a special attribute that forces you to inspect the value so you can't just ignore it. This does not mean you're forced to use try! - you can pattern-match the result (try! does the same thing after all).

P.S. try! above refers to Rust version, not Swift version - it has a bang since it's a macro.

dr.no at 2015-06-20 03:51:57:
So file1 fails to open
and returns
so first defer is called.

What happens when second defer is called when
control did not reach guard file2 statement but failed at file1?
or does second defer gets skipped.

Apple will definitely not open source Foundation
for simple reason don't want to propagate NS prefix into the future.

real question is how will Apple handle deluge of request to put C stdlib, libm, c++ std libraries functionality implemented in Swift std library.

obj-c runtme is open source but licensed not for commercial projects.

There is also Microsoft trying to clone all Apple APIs.

Jnosh at 2015-06-20 07:46:35:
@mikeash: Thanks!

@Rick: I think choosing the most constrained implementation makes sense given that's how the language works in other cases as well and generally should be what you want. Your idea of disambiguating at the call site sounds interesting, I'll have to give that some thought. I'm just hoping we don't get the standard "choose-or-rename" stuff that many languages use with multiple inheritance or mixins/traits. Always struck me as a bit of a crutch...

Michael M. Mayer at 2015-06-21 08:16:17:
@mikeash: loved this post. But really, protocol extensions were your favorite, right? Completely missed the C function pointer stuff in the videos I've watched and you gave me a much greater appreciation for the guard statement (previously viewed it as syntactic cruft). Thanks. Where does OptionSetType fall short in dealing adequately with C bitfields?

Shalini at 2015-06-21 21:19:54:
Awesome post!. Good explanation about the 'guard' statement. Thanks

Alexander Kempgen at 2015-06-22 11:10:56:
@dr.no
defers are only executed if the point where the defer is written was reached by control flow. So in your example, if file1 fails to open and the first guard's false branch returns from the function, the first defer is NOT executed. This is (presumably) desired in mikeash's example, because closing the file is only needed if opening the file succeeded. In situations where you have to clean up no matter the outcome of the guard, you would have to write the defer before the guard.

mikeash at 2015-06-22 15:02:02:
Michael M. Mayer: Different kind of bitfields. OptionSetType is for bitfields where you pack flags into an integer type. We're talking about fields in structs with a specified bit width. It's not a particularly commonly used feature in C, but you can do things like this:

struct Thingy {
    unsigned x: 3;
    unsigned y: 3;
    unsigned isFoo: 1;
    unsigned isBar: 1;
};


This defines a struct that occupies one byte of memory. Each field uses the specified number of bits to represent itself, so x and y can only hold the values 0-7, for example, but only occupy 3 bits of memory.

You can, of course, achieve the same effect by creating a single unsigned char field and doing the bit manipulation yourself, but C makes it more convenient. There's nothing like

mikeash at 2015-06-22 15:03:42:
I must have forgotten to finish typing. I meant to say, there's nothing like this in Swift.

elle at 2015-06-22 19:49:08:
Apple also made Intel SIMD Intrinsics available from Swift. However, somehow, only __m128 is defined, and __m128i is not. Is this a bug or am I doing something wrong?

jamie at 2015-06-24 04:58:08:
As someone that has to write callbacks for Core Audio functions I was seriously missing those function pointers (and they weren't mentioned at all in the WWDC videos). I'm happy to see we have them back but the limitation of being unable to capture locals means we still have to put something useful in all those context pointers...

Coolio at 2015-06-24 13:20:33:
Great post, thanks!

Dave Reed at 2015-06-26 21:42:19:
@dr.no actually it appears that file1.close() would not be called. It appears a defer is only called if the execution reaches the defer line. If file1 is not opened, it fails and returns before the defer line is reached. Try this code (experiment with making x and y nil or not nil).


func main() {
    let x: Int? = nil
    let y: Int? = 3
    guard let x1 = x else {
        print("assign x1 failed")
        return
    }
    defer { print("x1 defer") }
    print(x1)

    guard let y1 = y else {
        print("assign y1 failed")
        return
    }
    defer { print("y1 defer") }
    print(y1)
}

main()

Eugen at 2015-06-27 10:26:06:
In the last code snippet of the section Protocol Extensions didn't you intend to write protocol MyClassDelegate instead of extensions MyClassDelegate? Since, as you explained, default implementations in extensions are not dynamically dispatched and there would be no way to specialize the default optional implementation.

I'm not familiar with either Objective-C nor Swift so I might have misunderstood something.

Canis at 2015-06-27 10:51:41:
As someone who also has to write callbacks for Core Audio functions, and has written a large (approaching iWork/iLife app sized) audio app in Swift (and really likes Swift)... I write those in C.

Reason being, allocating/freeing memory from the heap is one of the things that's forbidden inside of Core Audio realtime callbacks*. And my understanding of the Swift memory model is that, according to the spec, everything in Swift is notionally "on the heap".

In quotes, because stuff usually tends to end up on the stack (I believe the phrase the Swift folks use is "aggressively optimised onto the stack") — but there are no guarantees, it's not something you have explicit control over, and while you might be able to make some good guesses and encourage the compiler to do the right thing, I don't relish adding "manually verify that everything you hope is on the stack is, indeed, on the stack" to my post-build steps.

Fortunately Swift's excellent C/Obj-C interop makes it Not A Problem to throw things across the porous membrane.

I really need to dig into Swift 2.0. It looks excellent, I'm waiting for the SDK to get to beta 3 (where things have usually shaken out a bit) before diving into trying to migrate all that code. Random observations:

- I kinda wish "guard" had been named "must" (e.g. must let file1 = Open("...") else { return })
- "for x in y where z" is cute
- Protocol Extensions, added to the puzzle-piece already in play, allow you to really extend the language in ways that feel built-in. Like OptionSetType.
- The approach to error handling is very Swiftian: typically pragmatic approach of "well, this looks close enough to exceptions for people to feel comfortable, but addresses a bunch of issues with that, and is much more efficient".
- In fact, that's my favourite thing about Swift overall: the way it negotiates the line between high-level features and low-level pragmatism.

(* because of thread priority inversion, the potential for blocking, missing your hard realtime deadlines and causing audio dropouts or pops)

Swift bginner at 2015-06-28 18:10:53:
Hi Mike- Maybe off topic - do you think it would be cool if there was Siri integration in Xcode - so you can ask Siri questions about your code and get intelligent answers back from Siri? As a newbie coder, I think this would be a cool feature to announce at WWDC 2016?

mikeash at 2015-06-29 15:32:09:
Eugen: The main protocol body cannot contain method implementations. Implementations must live in an extension section. Methods in an extension which are also declared in the main protocol body can be overridden. The last two code snippets in that section are intended to go together: the main protocol body declares the methods, and then the extension provides default implementations, effectively making them optional to any type that conforms to the protocol.

Canis: I'd be really surprised if local Swift variables containing simple structs ever ended up anywhere besides the stack. Arrays can be a gotcha, but if you work with pointers it should be fine. That said, I totally understand the reluctance to rely on that, and since C interop is so nice, mixing and matching languages is a good solution too.

Swift beginner: Code analysis tools would be pretty cool, but I don't know how much additional utility would be added with voice commands. If you wanted to try your hand at such a thing I highly recommend http://wit.ai which makes it really easy to build application driven by natural language commands.

H2CO3 at 2015-06-30 14:53:56:
It's a very big step forward that integration with C is now almost seamless. I like it. There are lots of useful C libraries out there that come handy for iOS and OS X development.

Protocol extensions are cool. The one feature I have missed so much from Objective-C protocols are default implementations; Swift has got these, and — apparently — even more.

The exception handling thingy, along with guard and defer, seems a bit of a code smell, though. Especially defer. It could be eliminated entirely with RAII classes — one less keyword, more importantly one less special case in the language. It's also possible to forget to defer file.close(), while that wouldn't occur with an RAII file object. A related problem is that, as you mentioned, deferred statements can pile up redundantly, and that's what brings me to my point about…

Exception handling. Programmers like to rant about various unrelated features of a programming language, pointing out why they are bad, and there's usually only one common point in their reasoning about why each individual feature is wrong: because it's not exception-safe.

So let's consider for a moment the possibility that it's maybe exceptions that hurt us so much. I've been developing for iOS for 6 years, I have used languages and libraries before that supported exceptions better, but in all honesty, I would never go back. Swift forcing me to prefix every call that can throw with try is a pain in the neck.

I'd much rather use something like Haskell's Error monad that chains potentially failing statements together seamlessly, allowing for more concise, more readable code and uniform, guaranteed handling of errors.

Mason at 2015-07-09 02:37:57:
Thanks for the writeup! This is my favorite comment about the new Swift features article by far. :)


Comments RSS feed for this page

Add your thoughts, post a comment:

Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.

Name:
Web site:
Comment:
Formatting: <i> <b> <blockquote> <code>. URLs are automatically hyperlinked.
Code syntax highlighting thanks to Pygments.
Hosted at DigitalOcean.