简体   繁体   English

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

[英]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. 我想定义一个通用协议GBucket ,有点像一组相同类型的项目。 A bit like a CollectionType , but with far less capabilities. 有点像CollectionType ,但是功能要少得多。 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. 实际上,它只是提供了一种将GBucket拆分为两个新GBucket的方法。 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' 无法将类型“ ArraySlice <Element>”的值转换为指定的类型“ 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. ArraySliceGBucket并且由于where规范,元素类型相同。

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 . Rasmus在评论中张贴了正确的链接,很好地说明了问题中的方法为何失败: Swift Types-从函数和方法中返回受约束的泛型 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. 通用部分实际上更像是C宏。 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)! 仅当调用splitBucket方法(或通过其他方式专用)时,类型A和B才会被类型替换! 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. 仅在这一点上, splitBucket的类型AB才会扩展为Array<Int> IE the specialized (generics expanded) version of the function would be: IE的功能的特殊版本(泛型扩展)将是:

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. 这就是为什么let a失败的原因。

PS: This doesn't answer how to accomplish the goal, but it is a starting point :-) PS:这不能回答如何实现目标,但这是一个起点:-)

That ArraySlice conforms to GBucket and A conforms to GBucket does not mean that you can cast between them. ArraySlice符合GBucket,A符合GBucket,并不意味着您可以在它们之间进行转换。 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)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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