如何实现返回该通用协议的通用Swift 2.2协议的方法

时间:2016-04-18 10:39:54

标签: swift generics

不太确定如何为此标出一个好的标题...

我想定义一个通用协议GBucket,它有点像一组相同类型的项目。有点像CollectionType,但功能却少得多。像这样:

public protocol GBucket {

  associatedtype BElement

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

它基本上只提供了一种将GBucket拆分为两个新GBucket的方法。它可以是符合协议的任何类型 - 即返回的部分不必是进行拆分的同一类。

我尝试将其作为示例实现:

 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 ;-)
  }
}

这会产生:

  

无法转换'ArraySlice&lt;类型的值元素&gt;'指定类型'A'

据我所知,转换就好了。 ArraySliceGBucket,由于where规范,元素类型相同。

另一个较短的示例说明了不使用数组内容的问题:

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>()
  }
}

我做错了什么?

2 个答案:

答案 0 :(得分:0)

Rasmus在评论中发布了一个正确的链接,很好地解释了为什么问题中的方法失败了:Swift Types - Returning constrained generics from functions and methods。他没有修改他的答案,因此我提供了一个 - 这真的是他的; - )

关键是在呼叫站点处解决了泛型问题。通用部分实际上更像是C宏。从概念上讲,它没有任何动态 - 它实际上是一个&#34;用实际类型替换一些泛型类型&#34;。

看看这个:

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 ;-)
  }
}

仅当调用splitBucket方法(或通过其他方式专门化)时,类型A和B才会被替换为类型!例如:

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

A中的BsplitBucket类型只会在此时扩展为Array<Int>。 I.E.该函数的专用(泛型扩展)版本将是:

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 ;-)
}

这就是let a必须失败的原因。

P.S。:这并没有回答如何实现目标,但这是一个起点: - )

答案 1 :(得分:-1)

ArraySlice符合GBucket,A符合GBucket并不意味着你可以在它们之间进行转换。您可以从更专业的类型转换为更常规的类型,但不能在两种不同的更专用的类型之间转换。

也许下面的代码可以解决您的问题?

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)
    }
}