Failable initializers for Classes

Hello,


I was wondering about failable initializers in Swift. I noticed that for structs it works as expected (at least by me), for example this is fine:

struct Test {
    var someState: Int
    init?(value: Int) {
        if value < 0 { return nil }
        someState = value // we set the value after validation
    }
}


But for classes swift wouldn't allow you to fail until you set internal states like so:


class Test {
    var someState: Int
     init?(value: Int) {
        if value < 0 {
            someState = 0 // this seems unneeded
            return nil
        }
        someState = value
    }

}


This seems wrong. Why do I need to populate class' data with unneeded default values before I can fail the initialization?

I can see that this is valid for both Swift 1.2 and 2.0 so what am I missing here?

I believe the requirement currently exists for classes because the deinit() method is called for the instance after init?() fails, and all the instance properties need to be initialized with a default value to ensure that deinit() can access them safely.


In a previous thread in the old forums, Chris Lattner stated that "This is a known issue that we'd already like to improve when time permits, thanks."

https://devforums.apple.com/message/1135583

This is a really bad deal if you're trying to use do-try-catch while setting ivars while initalizing a class. As far as I can tell, I have no way to set immutable ivars using error handling.


OSUOpenCLDevice.swift:169:57: error: all stored properties of a class instance must be initialized before throwing from an initializer
OSUOpenCLDevice.swift:15:9: note: 'self.name' not initialized
OSUOpenCLDevice.swift:16:9: note: 'self.vendor' not initialized
OSUOpenCLDevice.swift:17:9: note: 'self.driverVersion' not initialized
OSUOpenCLDevice.swift:18:9: note: 'self.deviceVersion' not initialized
OSUOpenCLDevice.swift:19:9: note: 'self.profile' not initialized
OSUOpenCLDevice.swift:20:9: note: 'self.extensions' not initialized
... (goes on for pages. :( )

You could do it like this


func randomlyDangerousInt() throws -> Int
{
    let potential = arc4random()
    if (potential == UInt32.max) {throw NSError(domain: "overflow", code:0, userInfo: nil)}
    if (potential % 4 == 0) {throw NSError(domain: "invalid", code:666, userInfo: nil)}
    else {return Int(potential)}
}

class TestClass
{
    let x: Int
    let y: Int
    let z: Int
  
    init?()
    {
        var shouldFail = false
      
        do {x = try randomlyDangerousInt()}
        catch {shouldFail = true; x = 0}
      
        do {y = try randomlyDangerousInt()}
        catch {shouldFail = true; y = 0}
      
        do {z = try randomlyDangerousInt()}
        catch {shouldFail = true; z = 0}
      
        if (shouldFail) {return nil}
    }
}


let a = TestClass()
let b = TestClass()
let c = TestClass()


Or, you could make your class a struct if that would work (and then can return nil at any point during initialization), or encapsulate parts of your class in a separate struct like this:


struct CTCResources
{
    let x: Int
    let y: Int
    let z: Int
   
    init?()
    {
        do
        {
            x = try randomlyDangerousInt()
            y = try randomlyDangerousInt()
            z = try randomlyDangerousInt()
        }
        catch {return nil}
    }
}


class TestClass2
{
    let resources: CTCResources!
    let name: String
   
    init?(name: String)
    {
        resources = CTCResources()
        self.name = name
       
        if (resources == nil) {return nil}
    }
}


let d = TestClass2(name: "d")
let e = TestClass2(name: "e")
let f = TestClass2(name: "f")

That's true. It's certainly possible to use failable initalizers as in your first example, and that's what I'm using now. It's an alright solution, but I feels more elegant to use exceptions in the context that I'm working in. For context, I'm working on an Swift wrapper for OpenCL, so a failure in the initalizers is not an easily recoverable issue, and it isn't clear what the appropriate action would be in that case anyway. Furthermore, a class is more appropriate bcause the implicitly shared behavior better encapsulates the nature of mapping to a discrete resource on the system. Making copies willy-nilly of the object would likely lead to lots and lots of problems.


I guess I'm just hoping that I could have the compact, explicitly error-handling, immutable code. It looks like I can choose any 1.5 of the three...

Failable initializers for Classes
 
 
Q