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.