Swift 具有泛型类型的协议函数
我想创建如下协议:Swift 具有泛型类型的协议函数,swift,generics,types,swift-protocols,associated-types,Swift,Generics,Types,Swift Protocols,Associated Types,我想创建如下协议: 协议解析器{ func parse()->ParserOutcome } 枚举语法分析器{ 个案结果(结果) 大小写分析器(语法分析器) } 我想让解析器返回特定类型的结果或其他解析器 如果在解析器上使用关联类型,则无法在枚举中使用解析器。如果我在parse()函数上指定了泛型类型,那么如果没有泛型类型,我就无法在实现中定义它 我怎样才能做到这一点 使用泛型,我可以这样写: 类解析器{ func parse()->ParserOutcome{…} } 枚举语法分析器{ 个
协议解析器{
func parse()->ParserOutcome
}
枚举语法分析器{
个案结果(结果)
大小写分析器(语法分析器)
}
我想让解析器返回特定类型的结果或其他解析器
如果在解析器
上使用关联类型,则无法在枚举
中使用解析器
。如果我在parse()
函数上指定了泛型类型,那么如果没有泛型类型,我就无法在实现中定义它
我怎样才能做到这一点
使用泛型,我可以这样写:
类解析器{
func parse()->ParserOutcome{…}
}
枚举语法分析器{
个案结果(结果)
大小写分析器(语法分析器)
}
这样,解析器将由结果类型参数化parse()
可以返回result
类型的结果,也可以返回任何类型的解析器,这些解析器将输出result
类型的结果,或者由相同的result
类型参数化的另一个解析器
然而,就我所知,对于关联类型,我总是有一个Self
约束:
协议解析器{
关联类型结果
func parse()->ParserOutcome
}
枚举语法分析器{
个案结果(结果)
大小写分析器(P)
}
在这种情况下,我不能再让任何类型的解析器返回相同的结果
类型,它必须是相同类型的解析器
我希望使用解析器
协议获得与使用泛型定义相同的行为,并且我希望能够在类型系统的范围内做到这一点,而不引入新的装箱类型,就像使用普通泛型定义一样
在我看来,在Parser
协议中定义associatedtype-OutcomeParser:Parser
,然后返回由该类型参数化的enum
可以解决问题,但如果我尝试以这种方式定义OutcomeParser
,我会得到错误:
类型不能将自身引用为需求
我认为您希望对ParserOutcome
enum使用通用约束
enum ParserOutcome<Result, P: Parser where P.Result == Result> {
case result(Result)
case parser(P)
}
enum ParserOutcome{
个案结果(结果)
大小写分析器(P)
}
这样,您将无法对任何不符合解析器
协议的内容使用ParserOutcome
。实际上,您可以再添加一个约束以使其更好。添加约束,即解析器
结果的结果将与解析器
的关联类型相同。执行此操作所需功能的状态:
- 实现递归协议约束()(Swift 4.1)
- 协议中的任意要求()已实施(Swift 4)
- 通用类型别名()已实现(Swift 3)
如果不引入盒式类型(“类型擦除”技术),这在目前看来是不可能的,而且对于Swift的未来版本来说也是如此,如的和部分所述(因为不支持)
当Swift支持这两项功能时,以下内容应生效:
协议解析器{
关联类型结果
associatedtype SubParser:解析器,其中SubParser.Result==Result
func parse()->ParserOutcome
}
枚举语法分析器{
个案结果(结果)
大小写分析器(P)
}
使用,子Parser类型也可以提取为:
typealias SubParser=解析器,其中SubParser.Result==Result
我不会这么快就将类型擦除视为“黑客”或“围绕[…]类型系统工作”——事实上,我认为它们与类型系统一起工作是为了在使用协议时提供有用的抽象层(如前所述,在标准库本身中使用,例如,&)
正如您自己所说的,您在这里要做的就是能够从解析器返回给定的结果,或者使用相同结果类型的另一个解析器。我们不关心该解析器的具体实现,我们只想知道它有一个parse()
方法返回相同类型的结果,或者另一个具有相同需求的解析器
类型擦除非常适合这种情况,因为您只需引用给定解析器的parse()
方法,就可以抽象出该解析器的其余实现细节。重要的是要注意,在这里您并没有失去任何类型安全性,您对解析器类型的精确性完全符合您的需求指定
如果我们看一看类型擦除解析器的潜在实现,AnyParser
,希望您能理解我的意思:
struct AnyParser<Result> : Parser {
// A reference to the underlying parser's parse() method
private let _parse : () -> ParserOutcome<Result>
// Accept any base that conforms to Parser, and has the same Result type
// as the type erasure's generic parameter
init<T:Parser where T.Result == Result>(_ base:T) {
_parse = base.parse
}
// Forward calls to parse() to the underlying parser's method
func parse() -> ParserOutcome<Result> {
return _parse()
}
}
从这个示例中可以看出,Swift仍然在强制执行类型安全。我们试图将BarParser
实例(与String
s一起工作)包装在AnyParser
类型擦除包装器中,该包装器需要Int
泛型参数,从而导致编译器错误。一旦将FooParser
参数化为使用String
s而不是Int
,编译器错误将得到解决
事实上,由于本例中的AnyParser
仅充当单个方法的包装器,另一个潜在的解决方案(如果您确实讨厌类型擦除)是直接将其用作ParserOutcome
的关联值
protocol Parser {
associatedtype Result
func parse() -> ParserOutcome<Result>
}
enum ParserOutcome<Result> {
case result(Result)
case anotherParse(() -> ParserOutcome<Result>)
}
struct BarParser : Parser {
func parse() -> ParserOutcome<String> {
return .result("bar")
}
}
struct FooParser : Parser {
func parse() -> ParserOutcome<String> {
let nextParser = BarParser()
return .anotherParse(nextParser.parse)
}
}
...
let f = FooParser()
let outcome = f.parse()
switch outcome {
case .result(let result):
print(result)
case .anotherParse(let nextParse):
let nextOutcome = nextParse()
}
协议解析器{
关联类型结果
func parse()->ParserOutcome
}
枚举语法分析器{
个案结果(结果)
案例另一个parse(()->ParserOutcome)
}
结构分析器:分析器{
func parse()->ParserOutcome{
返回结果(“bar”)
}
}
结构分析器:分析器{
傅
protocol Parser {
associatedtype Result
func parse() -> ParserOutcome<Result>
}
enum ParserOutcome<Result> {
case result(Result)
case anotherParse(() -> ParserOutcome<Result>)
}
struct BarParser : Parser {
func parse() -> ParserOutcome<String> {
return .result("bar")
}
}
struct FooParser : Parser {
func parse() -> ParserOutcome<String> {
let nextParser = BarParser()
return .anotherParse(nextParser.parse)
}
}
...
let f = FooParser()
let outcome = f.parse()
switch outcome {
case .result(let result):
print(result)
case .anotherParse(let nextParse):
let nextOutcome = nextParse()
}