Can you specify a return type in Swift as being any collection of a particular type?
The answer to your headline question is, unfortunately, no: there is no way to jury-rig a CollectionType as a stand-alone type that can be used as a variable or return type.
Protocols like SequenceType and CollectionType require the classes that implement them to provide typealiases to fill in the implementation details such as the element type, as you've done above. Once a protocol adds those requirements, it can never again be used as a stand-alone type. You can only declare interfaces in terms of specific classes that conform to it. (If you've tried to work around this, you may remember seeing not-very-helpful compiler errors about "associated type requirements".)
That's the basic reason why you can't write
func countItems(collection: CollectionType) -> Int { ... }
but must write instead
func countItems<T: CollectionType>(collection: T) -> Int { ... }
The latter form ensures that the compiler has access to the actual type of object (T) that's implementing the CollectionType protocol.
However, there may still be a cleaner implementation of what you're trying to do if you think in terms of encapsulation rather than inheritance. You can use a simple wrapper to block access to everything but the core CollectionType methods:
struct ShieldedCollection<UCT: CollectionType> : CollectionType
{
private var underlying: UCT
func generate() -> UCT.Generator { return underlying.generate() }
subscript(index: UCT.Index) -> UCT.Generator.Element { return underlying[index] }
var startIndex: UCT.Index { return underlying.startIndex }
var endIndex: UCT.Index { return underlying.endIndex }
}
var foo = [1, 2, 3]
var shieldedFoo = ShieldedCollection(underlying: foo)
(Here, UCT = "underlying collection type".)
ShieldedCollection still has all the usual typealiases to be a CollectionType, but since those can be inferred from context, you don't have to specify them explicitly.
The flaw with this generic approach, and unfortunately it's a rather major one, is that the underlying type still leaks out into the API. The type of shieldedFoo in the example above is
ShieldedCollection<Array<Int>>
Since your underlying collection is a custom object, its name would still potentially leak in the API, even if the class itself was not directly accessible by clients. Note that this isn't a functional problem, though, since it shouldn't be possible to access the underlying object through the ShieldedCollection wrapper. Furthermore, consumers will never have to write the type themselves - they can just use the result of previousItems() as a CollectionType and the compiler will disentangle everything.
If you're really intent on obscuring all mention of the underlying collection type, you can write a task-specific analog of the wrapper above by moving the UCT definition inside ShieldedCollection:
struct ShieldedCollection<T> : CollectionType // Changed!
{
typealias UCT = [T] // Added!
private var underlying: UCT // Everything else identical
func generate() -> UCT.Generator { return underlying.generate() }
subscript(index: UCT.Index) -> UCT.Generator.Element { return underlying[index] }
var startIndex: UCT.Index { return underlying.startIndex }
var endIndex: UCT.Index { return underlying.endIndex }
}
var foo = [1, 2, 3]
var shieldedFoo = ShieldedCollection(underlying: foo)
Here you make the return type tidy by giving up full generality -- this version of ShieldedCollection will work only with underlying CollectionTypes that are Arrays. (Of course, you would just substitute your own custom collection type in the typealias for UCT.)
As of Swift 5.1, this is finally possible with the some
keyword:
func upNext() -> some Collection { ... }