Failable-initializer problems: mistake in book, possible compiler bug

91 views
Skip to first unread message

Jens Alfke

unread,
May 19, 2015, 3:12:59 PM5/19/15
to swift-l...@googlegroups.com
I’m trying to figure out the rules for stored-property initialization and failable initializers, specifically how the heck I can have an initializer fail when its class contains stored properties with no default values. In doing so I’ve found mistake in an example the Swift Language book, and possibly a problem with the compiler.

So. The “Failable Initializers For Classes” section of the book* gives the following example of how to satisfy the requirement that all properties of a class have to have a value before the initializer can return nil:

class Product {
    let name: String!
    init?(name: String) {
        self.name = name
        if name.isEmpty { return nil }
    }
}

It then says “This default value of nil in turn means that all of the properties introduced by the Product class have a valid initial value. As a result, the failable initializer for Product can trigger an initialization failure at the start of the initializer if it is passed an empty string, before assigning a specific value to the name property within the initializer.”

But this is wrong: The example does not fail before assigning a specific value to ‘name’. It fails afterwards. As a result it isn’t even necessary in the example to make ‘name’ explicitly unwrapped: you can take out the “!” and it still compiles.

To fix the example so it demonstrates what it means to demonstrate, swap the two lines in the initializer, so the nil return occurs before the assignment to self.name. Unfortunately, it then fails to compile!

class Product {
    let name: String!
    init?(name: String) {
        if name.isEmpty { return nil } // <—Error!
        self.name = name
    }
}

The “return nil” statement is flagged with the error "All stored properties of a class instance must be initialized before returning nil from an initializer”. As far as I can tell, this error is wrong, because the ‘name’ property has a valid initial value of nil.

And worse, my actual code is running into the same error, so I can’t figure out how to make my initializer failable.

(I’m using the brand-new Xcode 6.3.2 on OS X 10.10.4 14E17e, btw.)

—Jens

* It’d be really nice if the Swift books had numbered sections, since they have no stable page numbers to refer to things by.

Chris Lattner

unread,
May 19, 2015, 8:25:12 PM5/19/15
to Jens Alfke, swift-l...@googlegroups.com
On May 19, 2015, at 12:12 PM, Jens Alfke <je...@mooseyard.com> wrote:

I’m trying to figure out the rules for stored-property initialization and failable initializers, specifically how the heck I can have an initializer fail when its class contains stored properties with no default values. In doing so I’ve found mistake in an example the Swift Language book, and possibly a problem with the compiler.

So. The “Failable Initializers For Classes” section of the book* gives the following example of how to satisfy the requirement that all properties of a class have to have a value before the initializer can return nil:

class Product {
    let name: String!
    init?(name: String) {
        self.name = name
        if name.isEmpty { return nil }
    }
}

It then says “This default value of nil in turn means that all of the properties introduced by the Product class have a valid initial value. As a result, the failable initializer for Product can trigger an initialization failure at the start of the initializer if it is passed an empty string, before assigning a specific value to the name property within the initializer.”

But this is wrong: The example does not fail before assigning a specific value to ‘name’. It fails afterwards. As a result it isn’t even necessary in the example to make ‘name’ explicitly unwrapped: you can take out the “!” and it still compiles.

This is a case where the book is out of sync with language changes that went into Xcode 6.3.  The recommended pattern for this is to turn the ‘let’ into a ‘var'.

-Chris

Jens Alfke

unread,
May 19, 2015, 11:01:46 PM5/19/15
to Chris Lattner, swift-l...@googlegroups.com

On May 19, 2015, at 5:25 PM, Chris Lattner <clat...@apple.com> wrote:

This is a case where the book is out of sync with language changes that went into Xcode 6.3.  The recommended pattern for this is to turn the ‘let’ into a ‘var'.

What are those language changes? From my experimentation, the compiler's are-all-properties-initialized test just seems broken. (Or at best, totally unintuitive.)

I don’t want to change properties into vars just because they need one-time initialization. I want them immutable, dammit.

—Jens

Chris Lattner

unread,
May 19, 2015, 11:30:03 PM5/19/15
to Jens Alfke, swift-l...@googlegroups.com
On May 19, 2015, at 8:01 PM, Jens Alfke <je...@mooseyard.com> wrote:
On May 19, 2015, at 5:25 PM, Chris Lattner <clat...@apple.com> wrote:

This is a case where the book is out of sync with language changes that went into Xcode 6.3.  The recommended pattern for this is to turn the ‘let’ into a ‘var'.

What are those language changes? From my experimentation, the compiler's are-all-properties-initialized test just seems broken. (Or at best, totally unintuitive.)

The most relevant change is that any let value may only be initialized, they cannot be reassigned in any circumstances.  In Swift 1.1 and earlier, there was an interesting loophole that allowed let properties to be multiply assigned within an initializer.

As part of this change, default initialization of optional let properties was suppressed: if it weren’t, you could never provide the value you want (it would be initialized to nil always).

I don’t want to change properties into vars just because they need one-time initialization. I want them immutable, dammit.

I understand.  The “all properties must be initialized before failing” is an unfortunate limitation that we’re eager to fix for a number of reasons.  In case it isn’t clear, we consider it a short-coming, not a feature.

-Chris

Reply all
Reply to author
Forward
0 new messages