I have implemented a custom queue object that I would like to use to store previous items in a controller. I would like to use this type as a private variable in my controller, and only expose it to the outside as simply a CollectionType
-compliant object, so that clients could iterate or index the object without knowing any class-specific details, such as a clear()
function.
Swift protocols can not be generic, so unfortunately I can't simply define a getter to return a CollectionOf<Type>
. I have implemented this behavior using the following abstract base class and subclassing my collection from it, but I was hoping that there might be a more Swift-y and builtin method to achieve this, which hopefully would also not require subclassing:
class AnyCollectionOf<MemberT, IndexT: ForwardIndexType>: CollectionType {
// Sequence Type
func generate() -> GeneratorOf<MemberT> {
fatalError("must override")
}
// Collection Type
typealias Index = IndexT
typealias Element = MemberT
subscript (index: Index) -> Element {
get {
fatalError("must override")
}
}
var startIndex: Index {
get {
fatalError("must override")
}
}
var endIndex: Index {
get {
fatalError("must override")
}
}
}
You write an optional tuple return type by placing a question mark after the tuple type's closing parenthesis, such as (Int, Int)? or (String, Int, Bool)? . An optional tuple type such as (Int, Int)? is different from a tuple that contains optional types such as (Int?, Int?) .
Using a protocol type as the return type for a function gives you the flexibility to return any type that conforms to the protocol. However, the cost of that flexibility is that some operations aren't possible on the returned values.
The Never return type is a special one in Swift, and tells the compiler that execution will never return when this function is called. It's used by Swift's fatalError() and preconditionFailure() functions, both of which cause your app to crash if they are called.
In Swift, there are two kinds of types: named types and compound types. A named type is a type that can be given a particular name when it's defined. Named types include classes, structures, enumerations, and protocols. For example, instances of a user-defined class named MyClass have the type MyClass .
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With