Swift 4 way to fake existential with protocols and `Self`

There are hundreds of solutions for faking existences in Swift with protocols and Self, but they mainly relate to Swift 2 and the bright future that Swift 3 can bring ... Now Swift 4 is out, with nice additions for Generics. But I could not find any suggestions on how to fit into the problem of missing existences.

Any ideas how to solve this Swift 4 way ?

Example:

import UIKit

protocol Bla {
    func compare(other: Self)
}

extension CGFloat : Bla {
    func compare(other: CGFloat) {
        print("Extended CGFloat")
    }
}

extension UIEdgeInsets : Bla {
    func compare(other: UIEdgeInsets) {
        print("Extended UIEdgeInsets")
    }
}

/* Possible, but what if we want to CGFloat _and_ UIEdgeInsets inside here?
 Well, that would _not_ work! */
class Possible<T: Bla> {
    var array: [T]!
}

/* This is open to everything...
 And therefore fails to compile, no dynamic type info at runtime I guess. */
class Fail {
    var array: [Bla]!
}

// Works, but I don't need that.
let list = Possible<CGFloat>()

// I need that:
let list = Fail()
let f: CGFloat = 1.23
let edge = UIEdgeInsets()
list.array.append(f)
list.array.append(edge)
+4
source share
3 answers

Swift 4 , . . . , , , Equatable, . , , . " ".

, , , , . , . , - " CGFloat UIEdgeInsets", Swift. "" [T], "OR" enum (Swift " " ). , :

enum Element {
    case scalar(CGFloat)
    case insets(UIEdgeInsets)
}
let list: [Element]

" , ". , , " , ", Swift NSValue ( NSNumber):

class Things {
    var array: [NSValue] = []

    func append(_ value: CGFloat) {
        array.append(NSNumber(value: Double(value)))
    }

    func append(_ insets: UIEdgeInsets) {
        array.append(NSValue(uiEdgeInsets: insets))
    }
}

let list = Things()
let f: CGFloat = 1.23
let edge = UIEdgeInsets()
list.append(f)
list.append(edge)

list.array[0] == list.array[0]  // true
list.array[0] == list.array[1]  // false
+1

, . :

class Fail {
    var array: [Bla]!
}

:

func compareAll(foo: Fail)
{
    for x in foo.array
    {
        x.compare(other: y)
    }
}

y? , , , , x . , y UIEdgeInsets, CGFloat.

, , Self, compare generic. :

protocol Bla {
    func compare<T: Bla>(other: T)
}

compare other .

extension CGFloat: Bla 
{
    func compare<T: Bla>(other: T) 
    { 
        if let casted = other as? CGFloat
        {
            // do whatever
        }
    }
} 

, , (. ) :

  • , - compare.
  • compare , other , Self, , .

    func compare<T: Bla>(other: T) 
    { 
        if let casted = other as? CGFloat
        {
            // do whatever
        }
        else if let casted = other as? UIEdgeInsets
        {
            // Do something else
        }
    }
    

, , , - .

+3

This is usually handled using an eraser type, for example:

struct AnyBla: Bla {
  private let compareClosure: (Any) -> ()  
  func compare(other: AnyBla) {
    compareClosure(other)
  }
  init<T: Bla>(_ bla: T) {
    compareClosure = { 
        if let other = $0 as? T {
          bla.compare(other: other)
        } else {
          print("Can't compare type \(type(of: bla)) with \(type(of: $0))")
        }
    }
  }
}

Then you change your array to any array of gum type, i.e.

class Fail {
    var array: [AnyBla]!
}

Then you can do what you want to do as follows:

let list = Fail()
let f: CGFloat = 1.23
let edge = UIEdgeInsets()
list.array.append(AnyBla(f))
list.array.append(AnyBla(edge))
+2
source

Source: https://habr.com/ru/post/1691675/


All Articles