简体   繁体   中英

How to implement a method of a generic Swift 2.2 protocol returning that generic protocol

Not quite sure how to phrase a good title for that ...

I'd like to define a generic protocol GBucket which is kinda like a set of items of the same type. A bit like a CollectionType , but with far less capabilities. Like so:

public protocol GBucket {

  associatedtype BElement

  func splitBucket
    <A: GBucket, B: GBucket where A.BElement == Self.BElement,
                                  B.BElement == Self.BElement>
    (idx: Int) -> ( A, B )
}

It essentially just provides a method to split a GBucket into two new GBucket s. Which can be of any type conforming to the protocol - ie the returned parts do not have to be the same class doing the split.

I tried that as a sample implementation:

 extension ArraySlice : GBucket {

  public typealias BElement = Generator.Element

  public func splitBucket
    <A: GBucket, B: GBucket where A.BElement == BElement,
                                  B.BElement == BElement>
    (idx: Int) -> ( A, B )
  {
    let ls : ArraySlice<A.BElement> = self[0..<idx]
    let a  : A = ls // this conversion FAILs
    return ( a, self[idx..<self.count] ) // this too, of course ;-)
  }
}

This produces:

Cannot convert value of type 'ArraySlice < Element >' to specified type 'A'

Which as far as I can tell should convert just fine. ArraySlice is a GBucket and the element type is the same thanks to the where specification.

And another, shorter sample illustrating the issue which doesn't use Array stuff:

public protocol GBucket {
  associatedtype BElement
  func otherBucket<A: GBucket where A.BElement == Self.BElement>() -> A
}

public class MyBucketType<T> : GBucket {
  public typealias BElement = T

  public func otherBucket<A: GBucket where A.BElement == BElement>() ->     A {
    return MyBucketType<A.BElement>()
  }
}

What me doin' wrong?

Rasmus posted a proper link in a comment which explains nicely why the approach in the question fails: Swift Types - Returning constrained generics from functions and methods . He didn't amend his answer, hence I provide one - it is really his ;-)

The key point is that generics are resolved at the call site . The generic part is really more like a C macro. Conceptually there is nothing dynamic about it - it really is a "replace some generic type with an actual type".

Look at this:

extension ArraySlice : GBucket {

  public typealias BElement = Generator.Element

  public func splitBucket
    <A: GBucket, B: GBucket where A.BElement == BElement,
                                  B.BElement == BElement>
    (idx: Int) -> ( A, B )
  {
    let ls : ArraySlice<A.BElement> = self[0..<idx]
    let a  : A = ls // this conversion FAILs
    return ( a, self[idx..<self.count] ) // this too, of course ;-)
  }
}

The types A and B will be replaced with a type only when the splitBucket method is being called (or specialized by other means)! Example:

let aSlice : ArraySlice<Int> = myArray[1..<5]
let result : ( Array<Int>, Array<Int> ) = aSlice.splitBucket(3)

The types A and B in splitBucket would be expanded to Array<Int> only at this point. IE the specialized (generics expanded) version of the function would be:

public func splitBucket(idx: Int) -> ( Array<Int>, Array<Int> ) {
  let ls : ArraySlice<A.BElement> = self[0..<idx]
  let a  : Array<Int> = ls // this conversion FAILs
  return ( a, self[idx..<self.count] ) // this too, of course ;-)
}

This is why the let a has to fail.

PS: This doesn't answer how to accomplish the goal, but it is a starting point :-)

That ArraySlice conforms to GBucket and A conforms to GBucket does not mean that you can cast between them. You can cast from more specialised types to more general types, but not between two different more specialised types.

Maybe the code below could solve your problem?

public protocol GBucket : CollectionType {
    func splitBucket(idx: Int) -> ( Self, Self )
}

extension ArraySlice : GBucket {
    public func splitBucket(idx: Int) -> ( ArraySlice, ArraySlice )
    {
        let A = self[0..<idx]
        let B = self[idx..<self.count]      
        return (A, B)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM