ChatGPT解决这个技术问题 Extra ChatGPT

Providing a default value for an Optional in Swift?

The idiom for dealing with optionals in Swift seems excessively verbose, if all you want to do is provide a default value in the case where it's nil:

if let value = optionalValue {
    // do something with 'value'
} else {
    // do the same thing with your default value
}

which involves needlessly duplicating code, or

var unwrappedValue
if let value = optionalValue {
    unwrappedValue = value
} else {
    unwrappedValue = defaultValue
}

which requires unwrappedValue not be a constant.

Scala's Option monad (which is basically the same idea as Swift's Optional) has the method getOrElse for this purpose:

val myValue = optionalValue.getOrElse(defaultValue)

Am I missing something? Does Swift have a compact way of doing that already? Or, failing that, is it possible to define getOrElse in an extension for Optional?


d
drewag

Update

Apple has now added a coalescing operator:

var unwrappedValue = optionalValue ?? defaultValue

The ternary operator is your friend in this case

var unwrappedValue = optionalValue ? optionalValue! : defaultValue

You could also provide your own extension for the Optional enum:

extension Optional {
    func or(defaultValue: T) -> T {
        switch(self) {
            case .None:
                return defaultValue
            case .Some(let value):
                return value
        }
    }
}

Then you can just do:

optionalValue.or(defaultValue)

However, I recommend sticking to the ternary operator as other developers will understand that much more quickly without having to investigate the or method

Note: I started a module to add common helpers like this or on Optional to swift.


my complier shows me that the type of unwrappedValue is still the optional type for this case in Swift 1.2: var unwrappedValue = optionalValue ? optionalValue! : defaultValue (xcode 6 beta 4). Has this changed?
I'm wrong on my version. I'm on 6.4. Be aware: ?? default type inference behavior is that it returns an optional, which is not what you want typically. You can declare the variable as non-optional and this works nicely.
M
MirekE

As of Aug 2014 Swift has coalescing operator (??) that allows that. For example, for an optional String myOptional you could write:

result = myOptional ?? "n/a"

U
UnRewa

if you wrote:

let result = optionalValue ?? 50

and optionalValue != nil then result will be optional too and you will need unwrap it in future

But you can write operator

infix operator ??? { associativity left precedence 140 }

func ???<T>(optLeft:T?, right:T!) -> T!
{
    if let left = optLeft
    {
        return left
    }
    else { return right}
}

Now you can:

 let result = optionalValue ??? 50

And when optionalValue != nil then result will be unwraped


W
Wes Campaigne

The following seems to work

extension Optional {
    func getOrElse<T>(defaultValue: T) -> T {
        if let value = self? {
            return value as T
        } else {
            return defaultValue
        }
    }
}

however the need to cast value as T is an ugly hack. Ideally, there should be a way to assert that T is the same as the type contained in the Optional. As it stands, type inferencing sets T based on the parameter given to getOrElse, and then fails at runtime if this does not match the Optional and the Optional is non-nil:

let x: Int?

let y = x.getOrElse(1.414) // y inferred as Double, assigned 1.414

let a: Int? = 5

let b: Double = a.getOrElse(3.14) // Runtime failure casting 5 to Double

You do not need to specify T for the method. That actually overrides the existing T from Optional. You can just do: func getOrElse(defaultValue: T) -> T then T refers to the real value type of the optional and you don't have to type check it
Thanks! I inferred exactly that from the second half of your response. Is there some way to inspect the definition of Optional to know that the generic type is defined as T in that? (beyond simply assuming based on convention)
If you command-click on Optional inside Xcode, you will see that it is defined as enum Optional<T>
Oh, excellent. I should have guessed. The definitions in there will be useful; there's a lot of things that aren't in the documentation yet. Thanks again.
P
Plasma

If you are trying to do this with a String you can do this ..

string1 = string2.isEmpty ? "Default Value":string2