简体   繁体   中英

Swift 2.0 - generic factory function that returns a type determined by implementation of function

Is this possible?

The scenario is, I want a function to take an unrelated argument to this function's generic constraints, and return a unknown type of result dependent on said argument. The types do not have any relationship to each other. One may be a string, one may be an int, one may be an array of strings etc..

I have experimented with this using typealias in a protocol, and each object conforming to it sets up its own type, and uses the type alias in its workings.

I have experimented with this as a protocol:

protocol BaseFunctionality {
    typealias ReturnType
    var property: ReturnType { get set }
    func getData() -> ReturnType
}

Then I have a couple conforming classes:

class StringClass: BaseFunctionality {
    typealias ReturnType = String
    var property: ReturnType = "HELLO WORLD"
    func getData() -> ReturnType {
        return property
    }
}

and

class IntClass: BaseFunctionality {
    typealias ReturnType = Int
    var property: ReturnType = 12345
    func getData() -> ReturnType {
        return property
    }
}

So, What I came up with was this:

func getIt<T: BaseFunctionality>(int: Int) -> T.ReturnType {
    if (int == 0) {
        let stringClass = StringClass()
        return stringClass.getData() as! T.ReturnType // for some reason I have to as! this
    } else {
        let intClass = IntClass()
        return intClass.getData() as! T.ReturnType // same as above comment
    }
}

and calling it like this:

let testing = getIt(1)

however, I get an error message,

"Cannot invoke 'getIt' with argument list of type '(int)'".

I get that the compiler is having issues figuring out what exactly I want as a return type, so then, I thought of providing a closure which maps the T: BaseFunctionality to the property it holds.

func getIt<T:BaseFunctionality, U where U == T.ReturnType>(int:Int, unwrapType: T -> U ) -> U {
    if (int == 0) {
        let source = StringClass()
        return unwrapType(source as! T) // for some reason, source is not a valid T
    }
    else {
        let source = IntClass()
        return unwrapType(source as! T) // see above comment
    }
}

There may be something wrong with the constraints for U in the signature, but I have tried a variety of them..

and the way I call it, for example, is something like this.

let data = getIt(1, mapToType: {(unwrapType) -> String in
    return unwrapType.property  as String
})

However, This causes crazy things to happen in the playground... (crashing)

Does anyone have any ideas on how to achieve this magical function with has an unknown return type at compile time?

here for the swiftstub: http://swiftstub.com/800198020/?v=beta

Thanks!

As Qbyte said or

enum TextOrNum {
    case Text(s: String)
    case Num(x: Int)
}

func getIt(i: Int) -> TextOrNum {
    if i == 0 {
        return .Text("Hello")
    } else {
        return .Num(42)
    }
}

// usage
switch getIt(0) {
case let .Num(x):
    // use x return value as Int
case let .Text(s):
    // use s return value as String
}

The advantage of the enum is that the compiler will check you delt with all cases.

You can only achieve such dynamic behavior with Any and a switch which casts the value back in order to use the returned type as typed variable:

// the function
func getIt(i: Int) -> Any {
    if i == 0 {
        return "Hello"
    } else {
        return 42
    }
}

// usage
switch getIt(0) {
case let x as Int:
    // use the return value as Int
case let str as String:
    // use the return value as String
default: break
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM