How to implement a infix custom operator that handles optionality in swift

I'm trying to implement a custom operator for Collections similar to the Elvis operator (?: in kotlin, ?? in swift), but in addition to checking nullability, the operator also checks if the collection is Empty.

However, when I try to use the operator, the code doesn't compile. The compiler throws an "ambiguous use of operator" error.

The implementation of the ?? operator in the swift language seems to be really similar to mine, so I'm a little lost.. Any help to understand why this happens, and how to fix it, will be greatly appreciated.

/// Returns the first argument if it's not null and not empty, otherwise will return the second argument.
infix operator ?/: NilCoalescingPrecedence

@inlinable func ?/ <T: Collection>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T {
    if let value = optional,
       !value.isEmpty {
        return value
    } else {
        return try defaultValue()
    }
}

@inlinable func ?/ <T: Collection>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T? {
    if let value = optional,
       !value.isEmpty {
        return value
    } else {
        return try defaultValue()
    }
}


func test() {
    let optionalValue: String? = nil

    let value1: String = optionalValue ?? "default value" // This works
    let value2: String = optionalValue ?/ "default value" // This works
    let value3: String? = optionalValue ?? nil // This works
    let value4: String? = optionalValue ?/ nil // This works
    let value5: String? = optionalValue ?? "default value" // This works
    let value6: String? = optionalValue ?/ "default value" // This dont compile: Ambiguous use of operator '?/'
}

The standard implementation for the ?? operator can be found at: swift/Optional.swift at main · apple/swift · GitHub, just search for "?? <" in the browser.

Maybe I'm using the wrong approach to solve this problem. If anyone knows a better solution will be great too.

Try using @_disfavoredOverload on one of the implementations.

I’m not sure why it doesn’t work as you wrote it though. Since value6 is declared as optional, one would expect the overload that doesn’t return optional (and thus requires implicit promotion) to be ranked lower by the compiler.

Edit: also I moved this from “Development – Compiler” to “Using Swift”.

1 Like

It works perfectly! Thanks @Nevin

It seems this @_disfavoredOverload is an attribute that is not ready for production use yet, so I will avoid using it in real applications for now.

The solution I'm gonna stick with for now is to simply unwrap the value to make the compiler knows which method to use (although I don't know why it doesn't know already):

let value6: String? = (optionalValue ?/ "default value")!