当你添加Hashable
,添加了Equatable
,这使得它成为具有关联类型(PAT)的协议,因为Equatable
是一个PAT。 PAT 不是一种类型;而是一种类型。它是一个向其他类型添加方法的工具。
您不能使用 PAT 作为变量的类型,不能将其放入数组中,不能直接将其作为参数传递,不能将其作为值返回。 PAT 的唯一一点是作为通用约束(where L: Line
)。 PAT 说明了另一个具体类型必须提供什么才能在某些上下文中使用。
您应该如何解决这个问题尚不清楚。这看起来根本不应该是一个协议。这取决于您在这里尝试解决的代码重用问题。
协议通常是关于某物可以做什么do. Line
看起来它只是想隐藏实现,而不表达任何内容;这不是一个协议。正如所写的,这里根本没有理由使用泛型或协议。其他实现有什么作用Line
看起来像? (我很难想象你还能如何实现这种类型。)
我怀疑正确的答案是将所有这些替换为Station
结构体和一个Line
结构。我没有看到协议在哪里发挥作用。
这是我可以实现您正在构建的内容的一种方法,以及一些新协议以了解它们的用途。其中一些可能超出了解决此问题所需的范围,但我想展示正在运行的协议。
// A protocol for "ID" types that automatically gives them handy inits
// Nested ID types mean that Line.ID can't get confused with Station.ID.
// The point of a protocol is to add additional features to a type like this.
protocol IDType: Hashable, ExpressibleByStringLiteral, CustomStringConvertible {
var value: String { get }
init(value: String)
}
extension IDType {
// For convenience
init(_ value: String) { self.init(value: value) }
// Default impl for ExpressibleByStringLiteral
init(stringLiteral value: String) { self.init(value: value) }
// Default impl for CustomStringConvertible
var description: String { return value }
}
struct Line: Equatable {
struct ID: IDType { let value: String }
let id: ID
let stations: [Station]
var origin: Station { return stations.first! } // We ensure during init that stations is non-empty
var terminus: Station { return stations.last! }
init(id: ID, origin: Station, stops: [Station], terminus: Station) {
self.id = id
self.stations = [origin] + stops + [terminus]
}
}
// Conforming Line to this protocol lets it print more beautifully.
extension Line: CustomStringConvertible {
var description: String { return "\(id): \(origin) -> \(terminus)" }
}
// Stations can't contain Line directly. These are value types, and that would be
// recursive. But this is nice because it lets us construct immutable Stations
// and then glue them together with Lines which may even be in independent
// systems (for example, the bus system might be separate from the rail system,
// but share stations)
struct Station: Hashable {
struct ID: IDType { let value: String }
let id: ID
let name: String
func lines(in system: System) -> [Line] {
return system.linesVisiting(station: self)
}
}
extension Station: CustomStringConvertible {
var description: String { return name }
}
struct System: Equatable {
let lines: [Line]
// Using Set here makes it clear there are no duplicates, and saves
// some hassle turning it back into an Array, but we could also just
// return Array here as Array(Set(...))
var stations: Set<Station> {
// Uniquify the stations
return Set(lines.flatMap { $0.stations })
}
func linesVisiting(station: Station) -> [Line] {
return lines.filter { $0.stations.contains(station) }
}
}
// Some examples of using it.
let stationNames = ["Shady Grove", "Bethesda", "Metro Center", "Glenmont",
"Wiehle-Reston East", "Largo Town Center"]
// Build up a few stations; obviously there are many more
let stations = Dictionary(uniqueKeysWithValues:
stationNames.map { ($0, Station(id: .init($0), name: $0)) })
// Define some lines
let redLine = Line(id: "OR",
origin: stations["Shady Grove"]!,
stops: [stations["Bethesda"]!, stations["Metro Center"]!],
terminus: stations["Glenmont"]!)
let silverLine = Line(id: "SV",
origin: stations["Wiehle-Reston East"]!,
stops: [stations["Metro Center"]!],
terminus: stations["Largo Town Center"]!)
// And glue them together into a system
let system = System(lines: [redLine, silverLine])