Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional extension for any types

I want to write Optional extension for any types.

My code for integer:

extension Optional where Wrapped == Int {

    func ifNil<T>(default: T) -> T {
        if self != nil {
            return self as! T
        }
        return default
    }
}

var tempInt: Int?

tempInt.ifNil(default: 2) // returns 2

tempInt = 5

tempInt.ifNil(default: 2) // returns 5

It works but it is Optional(Int) extension (Optional where Wrapped == Int), I want to use this extension for any types like Date, String, Double etc.

What are your suggestions?

like image 987
emrcftci Avatar asked Jul 27 '19 13:07

emrcftci


People also ask

What is Extension Optional?

An Optional is a enum of a specified type ( Wrapped ) which indicates that it does or does not contain a value. You can write an extension on the Optional "container" even though it may not contain a value.

What is optional type in Swift?

An Optional is a type on its own, actually one of Swift 4's new super-powered enums. It has two possible values, None and Some(T), where T is an associated value of the correct data type available in Swift 4.

Which is optional in variable declaration?

An optional in Swift is basically a constant or variable that can hold a value OR no value. The value can or cannot be nil. It is denoted by appending a “?” after the type declaration.

How do you declare an optional string?

To use an optional, you "unwrap" it An optional String cannot be used in place of an actual String . To use the wrapped value inside an optional, you have to unwrap it. The simplest way to unwrap an optional is to add a ! after the optional name. This is called "force unwrapping".


Video Answer


2 Answers

The answer to your basic question is to just remove the where clause:

extension Optional { 
    // ... the rest is the same 
    func isNil<T>(value: T) -> T {
        if self != nil {
            return self as! T
        }
        return value
    }
}

Now it applies to all Optionals.

But this code is quite broken. It crashes if T is not the same as Wrapped. So you would really mean a non-generic function that works on Wrapped:

extension Optional {
    func isNil(value: Wrapped) -> Wrapped {
        if self != nil {
            return self!  // `as!` is unnecessary
        }
        return value
    }
}

But this is just an elaborate way of saying ?? (as matt points out)

extension Optional {
    func isNil(value: Wrapped) -> Wrapped { self ?? value }
}

Except that ?? is much more powerful. It includes an autoclosure that avoids evaluating the default value unless it's actually used, and which can throw. It's also much more idiomatic Swift in most cases. You can find the source on github.

public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
    rethrows -> T {
  switch optional {
  case .some(let value):
    return value
  case .none:
    return try defaultValue()
  }
}

But I can imagine cases where you might a method-based solution (they're weird, but maybe there are such cases). You can get that by just rewriting it as a method:

extension Optional {
    public func value(or defaultValue: @autoclosure () throws -> Wrapped) rethrows -> Wrapped {
        switch self {
        case .some(let value):
            return value
        case .none:
            return try defaultValue()
        }
    }
}

tempInt.value(or: 2)
like image 112
Rob Napier Avatar answered Sep 19 '22 17:09

Rob Napier


Optional is already a generic. It already takes any type as its parameterized type. Its parameterized already has a name: Wrapped. Just say Wrapped instead of T. Your T is Wrapped.

extension Optional {
    func isNil<Wrapped>(value: Wrapped) -> Wrapped {
        if self != nil {
            return self as! Wrapped
        }
        return value
    }
}

If you really prefer T for some reason, use a type alias. It's only a name:

extension Optional {
    typealias T = Wrapped
    func isNil<T>(value: T) -> T {
        if self != nil {
            return self as! T
        }
        return value
    }
}

But in any case your extension is completely unnecessary because this is what the nil-coalescing operator ?? already does.

var tempInt: Int?
tempInt ?? 2 /// returns 2
tempInt = 5
tempInt ?? 2 /// returns 5
like image 22
matt Avatar answered Sep 19 '22 17:09

matt