rogueleaderr
rogueleaderr

Reputation: 4819

Can I constrain a generic parameter to *not* be optional?

Let's say I have this code:

func hello<T>(thing: T) -> String {
    return "hello \(thing)"
}

Can I write a version of the hello function that won't compile if it's passed an optional?

let foo = "some"
let bar: String? = nil

print(helloNoOptional(foo))  // should compile
print(helloNoOptional(bar))  // should not compile

I'm thinking maybe it's doable with a protocol conformance or where clause on T but I can't think of how exactly that would work.

The reason I want to do this is because I'm dealing with a actual function in legacy codebase that has no sensible behavior if thing is nil. So I'd rather prevent hello from being called on an optional rather than deal with unwrapping thing inside of hello and trying to figure out a sensible error behavior.

Update:

A possible path...I realized that the Optional enum conforms to the NilLiteralConvertible protocol. So if I can find a way to constrain my generic to not conform to a type, I can exclude optionals de facto. But I don't know if it's possible to do something like

<T where !T: NilLiteralConvertible>

Upvotes: 18

Views: 2065

Answers (2)

Mark A. Donohoe
Mark A. Donohoe

Reputation: 30428

Building on @Airspeed Velocity's answer, I personally prefer to keep everything in one place, like so...

func hello<T>(_ thing: T) -> String {
    
    if T.self is ExpressibleByNilLiteral.Type {
        fatalError("Optional types not supported")
    }

    return "hello \(thing)"
}

Upvotes: 1

Airspeed Velocity
Airspeed Velocity

Reputation: 40963

Best I can think of is overload and check at runtime:

func hello<T>(thing: T) -> String {
    return "hello \(thing)"
}

fun hello<T>(thing: T?) -> String {
    fatalError("No optionals allowed!")
}

hello("swift")  // fine
hello(2)        // fine
hello(Int("2")) // fatal error

But I don't know of a way of generating a compile-time error instead.

Upvotes: 5

Related Questions