6

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.

Aaron Hayman
  • 8,492
  • 2
  • 36
  • 63

1 Answers1

5

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 :).

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • This is an awesome use of the power of extensions in Swift 2! – Logan Jul 29 '15 at 15:16
  • Unfortunately, this is production code so I can't use Swift 2 yet. While that looks like it would work in Swift 2, I can't verify it at the moment. – Aaron Hayman Jul 29 '15 at 15:23
  • Yes, that does work. It's similar to my workaround, except you've created a factory method whereas I simply provided a convenience handler. Oddly, I did try doing that within the Struct using `static func` but I still ended up with type mismatches. I'll mark your answer as accepted since I think there's no real way to do what I want except wait until Swift 2 arrives. :) – Aaron Hayman Jul 29 '15 at 15:40