简体   繁体   中英

Error When Using Protocol as a Type in Swift

I'm using Swift and trying to make some collection objects. These collection objects have a backing Dictionary to hold custom objects. For example, an object might be of type Cat and the collection object would be of type Cats . Cats would have a private dictionary containing values of type Cat . I have other types that also need respective collections (each collection type has specific logic for the type it holds).

I created a protocol to ensure each collection has a few common characteristics. These common functions and subscripts are generally passthroughs to the backing dictionary. Here is the protocol:

protocol ObjectDictionaryProtocol {

    // These are necessary for generics to work in protocols
    typealias Key: Hashable
    typealias Value


    // MARK: - Properties

    var count: Int { get }
    var isEmpty: Bool { get }
    var keys: LazyMapCollection<Dictionary<Key, Value>, Key> { get }
    var values: LazyMapCollection<Dictionary<Key, Value>, Value> { get }


    // MARK: - Subscripts

    subscript(key: Key) -> Value? { get set }

}

When I go to actually use the protocol as a type, for instance:

var objects: ObjectDictionaryProtocol

or

init(objs: ObjectDictionaryProtocol) {
    ...
}

I get the error:

Protocol 'ObjectDictionaryProtocol' can only be used as a generic constraint because it has Self or associated type requirements

I've searched around and it looks like the Hashable protocol I'm conforming to for my Key typealias is causing this. What is the best way to get around this? Is there a way to change the protocol such that I don't need the Hashable , or do I need to do something in the class that is using ObjectDictionaryProtocol ? Or maybe there's a better way to effectively 'subclass' a Swift Dictionary (quotes because I realize the Dictionary struct cannot be subclassed)?

A protocol with associated types is less a type and more a template for a type. The actual type exists when the protocol's associated types are specified. Thus ObjectDictionaryProtocol 'becomes' a type when you specify:

ObjectDictionaryProtocol<String,Cat>

but the above is not valid Swift...

So you asked... ' [is there] a better way to effectively 'subclass' a Swift Dictionary '. You might get by with an extension with something like:

class Cat {}

extension Dictionary where Value : Cat {
  func voice (name: Key) {
    if let _ = self[name] {
      print ("\(name): Meow")
    }
  }
}

var someCats = Dictionary<String,Cat>()
someCats["Spot"] = Cat()
someCats.voice("Spot")
// Spot: Meow

Or you may need to actually implement the protocol.

class ObjectDiciontary<Key:Hashable, Value> : ObjectDictionaryProtocol {
  var backingDictionary = Dictionary<Key,Value>()
  // implement protocol
}

var object : ObjectDictionary<String,Cat> = ...
// ...

The reason is because when you're using typealias you're effectivly making your Protocol into a generic protocol as Protocol<T> . Bear with me here, I'll explain why and how to fix it.

The issue here is that Apple has decided to make typealias the keyword for defining associated types. This is fixed in Swift 2.2 (Xcode 7.3) You can read more about it on https://github.com/apple/swift-evolution/blob/master/proposals/0011-replace-typealias-associated.md

It's being renamed to associatedtype which makes more sense.

This means your protocol has to be adopted, and there define its associated types.

In your case it would look something like

protocol ObjectDictionaryProtocol {
  associatedtype Value
}

extension String : ObjectDictionaryProtocol {
  associatedtype = Double
}

The init could look like

 init<T : ObjectDictionaryProtocol>(objs: ObjectDictionaryProtocol)

or

init<T : ObjectDictionaryProtocol 
     where T.Value == Double>(objs: ObjectDictionaryProtocol)

Now for typealias Key: Hashable it means that whatever type that is assigned to Key has to conform or be Hashable

This will give you an error that String isn't conforming to ObjectDictionaryProtocol as it can't fulfil the requirements.

protocol ObjectDictionaryProtocol {
  associatedtype Value : FloatingPointType
}

extension String : ObjectDictionaryProtocol {
  associatedtype = Int
}

Int isn't a FloatingPointType (but Double is)

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.

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