Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Generic constraints in init

I have generic and I want to be able to initialize it with specific constrains. The constraints are only there for initialization. The rest of the class doesn't care. Here is a simplified example:

struct Generic<T> {
  let compare: (T, T) -> Bool
  init<T: Equatable>(data: [T]) {
    let handler: (T, T) -> Bool = { $0 == $1 }
    compare = handler
    insert(data)
  }

  init(compareHandler: (T, T) -> Bool, data[T]) {
    compare = self.compareHandler
    insert(data)
  }
}

You can see there's two initializers. The second one obviously works fine. However, in the first one the local type T is mismatched with the struct's generic Type. So, for example, attempting to insert data I get Cannot invoke 'insert' with an argument list of type '([T])'. Is it possible for me to specialize the Struct's generic type only for the initialization or a specific function?

Note, I've already tried init<T where T:Equatable>(data: [T]) to the same effect.

Update

I'm using the following workaround: I create a top level function and removing the specialized init:

func equatableHandler<T: Equatable>(left: T, right: T) -> Bool {
  return left == right
}

Clients of the struct can initialize using: Generic(compareHandler: equatableHandler, data: data)

It's not quite the "convenience" of using a specialized init, but I suppose it works well enough for my purposes. I'm not a fan of creating top-level functions, but the generic is used so often for "Equatable" generics that it makes sense for me to define the handler once for clients to use.

like image 483
Aaron Hayman Avatar asked Jul 29 '15 14:07

Aaron Hayman


1 Answers

The problem is that the first init method

init<T: Equatable>(data: [T]) 

introduces a local type placeholder T which hides (and is completely unrelated to) the placeholder T of the Generic type, so it is essentially the same problem as in Array extension to remove object by value.

As of Swift 2 you can solve that with a "restricted extension":

extension Generic where T : Equatable {
    init(data: [T]) {
        let handler: (T, T) -> Bool = { $0 == $1 }
        compare = handler
        // ...
    }
}

For Swift 1.x the only solution is probably to define a global helper function

func makeGeneric<T : Equatable>(data: [T]) -> Generic<T> {
    return Generic(compareHandler:  { $0 == $1 }, data: data)
}

(and I could not think of a sensible name for the function :).

like image 77
Martin R Avatar answered Oct 08 '22 06:10

Martin R