Brandon
Brandon

Reputation: 23500

Return Nil for Generic Type

I have the following code:

class Function<T> {
    var ptr: () throws -> T

    init<Func>(block: Func, args: AnyObject...) {
        self.ptr = {() throws -> T in
            let result: AnyObject? = nil

            if T.self == Void.self {
                return Void() as! T
            }

            return result //Error Here.. Cannot as! cast it either.. Cannot unsafeBitCast it either..
        }
    }
}

postfix operator ^ { }
postfix func ^ <T>(left: Function<T>) throws -> T {
    return try left.ptr()
}


func call() {
    let block: (String) -> String? = {(arg) -> String? in
        return nil
    }

    let fun = Function<String?>(block: block, args: "Hello")
    fun^
}

The function Block.execute returns AnyObject?. My Generic class Function<T> expects a return type of T.

If T is already String? why can't I return nil?

Is there any way to return nil as type T which is already Optional?

If I make T Optional, then the return type becomes Optional<Optional<String>> which is not what I want.. then the compiler complains that OptionalOptional is not unwrapped with ?? That is how I know that T is already optional.

Upvotes: 3

Views: 1284

Answers (4)

julz12
julz12

Reputation: 452

After a long hunt on google I have finally found an elegant way to do this. It's possible to write a class extension with type constraints.

class Foo<T> {
  func bar() -> T {
    return something
    // Even if T was optional, swift does not allow us to return nil here:
    // 'nil' is incompatible with return type 'T'
  }

}
extension Foo where T: ExpressibleByNilLiteral {
    func bar() -> T {
        return nil
        // returning nil is now allowed
    }
}

The appropriate method will be invoked depending on whether T is optional or not.

Upvotes: 2

Lee Fastenau
Lee Fastenau

Reputation: 444

This does the trick as of Swift 3.1. Modification of user3763801's answer.

func cast<T>(_ v: Any) -> T {
    return v as! T
}

Upvotes: 0

user3763801
user3763801

Reputation: 395

The following casts Any? to T returning nil only if T is optional and from is nil. It crashes, if from cannot be cast to T.

func cast<T>(from v: Any?)->T {        
    return v as! T
}

func cast<T>(from v: Any?)->T where T: ExpressibleByNilLiteral {
    guard let v = v else { return nil }
    return v as! T
}

Upvotes: 0

Brandon
Brandon

Reputation: 23500

I solved it with a nasty work around.. I throw an exception for nil return type. Then in the generic function operator I catch that specific exception and return nil if T is NilLiteralConvertible.. Otherwise I just execute normally..

class Function<T> {
    var ptr: () throws -> T

    init<Func>(block: Func, args: AnyObject...) {
        self.ptr = {() throws -> T in
            let result: AnyObject? = execute(block, args...)

            if T.self == Void.self {
                return Void() as! T
            }

            throw BlockError.BlockReturnsNil
        }
    }
}

postfix func ^ <T>(left: Function<T>) throws -> T {
    return try left.ptr()
}

postfix func ^ <T : NilLiteralConvertible>(left: Function<T>) throws -> T {
    do {
        return try left.ptr() 
    }
    catch BlockError.BlockReturnsNil {
        return nil
    }
}

Upvotes: 0

Related Questions