1

I have a generic type, and I would like to initialise it in different ways, depending on whether the actual type is Optional or not.

struct Foo<Bar> {
    var value: Bar
}

extension Foo {
    init(data: Any) throws {
        if let typedData: Bar = data as? Bar {
            self.value = typedData
        }
        else {
            throw NSError(domain: "", code: 0, userInfo: nil)
        }
    }
}

// (invalid restriction)
extension Foo where Bar: Optional<Bar> {
    init(data: Any) throws {
        if (data as? NSNull) == nil {
            if let typedData: Bar = data as? Bar {
                self.value = typedData
            }
            else {
                throw NSError(domain: "", code: 0, userInfo: nil)
            }
        }
        else {
            self.value = nil
        }
    }
}

The idea being that I am initialising from some unknown data type which should either initialise correctly if the type corresponds, or throw an error otherwise, but where NSNull corresponds to a nil value if the type is Optional.

let nonOptionalData: Any = Bar()
// should be successful
let foo1: Foo<Bar> = try Foo(data: nonOptionalData)

let nonOptionalIncorrectData: Any = NSNull()
// should throw an error
let foo2: Foo<Bar> = try Foo(data: nonOptionalIncorrectData)

let optionalData: Any = Bar()
// should be successful
let foo3: Foo<Bar?> = try Foo(data: optionalData)

let optionalIncorrectData: Any = Bob()
// should throw an error
let foo4: Foo<Bar?> = try Foo(data: optionalIncorrectData)

let optionalNullData: Any = NSNull()
// should be successful
let foo5: Foo<Bar?> = try Foo(data: optionalNullData)

Does anyone know if this is possible (the code above fails because we can't restrict the extension to the Optional type), and if so, how it can be done?

Rupert
  • 2,097
  • 19
  • 28

1 Answers1

1

It can be done in the same way as I've answered here.

protocol OptionalType {
    typealias W
    static func fromOptional(o: W?) -> Self
}

extension Optional: OptionalType {
    typealias W = Wrapped
    static func fromOptional(o: W?) -> W? { return o }
}

struct Foo<Bar> {
    var value: Bar
}

extension Foo {
    init(data: Any) throws {
        if let typedData: Bar = data as? Bar {
            self.value = typedData
        }
        else {
            throw NSError(domain: "", code: 0, userInfo: nil)
        }
    }
}

extension Foo where Bar: OptionalType {
    init(nullableData: Any) throws {
        if nullableData is NSNull {
            self.value = .fromOptional(nil)
        }
        else if let typedData = nullableData as? Bar.W {
            self.value = .fromOptional(typedData)
        }
        else {
            throw NSError(domain: "", code: 0, userInfo: nil)
        }
    }
}

But I've not been able to find a way to make the "init(data:)" overloads having the same signature, without "ambiguous use" error. This is why I renamed the the latter overload to "init(nullableData:)".

let b: Foo<Int?> = try! Foo(nullableData: NSNull())
let c: Foo<Int?> = try! Foo(nullableData: 8)
let a: Foo<Int> = try! Foo(data: 1)
let d: Foo<Int> = try! Foo(nullableData: 1) // <-- cannot compile
Community
  • 1
  • 1
findall
  • 2,176
  • 2
  • 17
  • 21