将Swift泛型类转换为具有typealias的协议

时间:2016-03-21 01:00:20

标签: swift generics

我是疯了还是不应该快速编写代码?

protocol Protocol {
  typealias Thing
}

class Class<X>: Protocol {
  typealias Thing = X
}

func test<X:Protocol where X.Thing == Int> () -> X {
  return Class<Int>()  // error: cannot convert return expression of type 'Class<Int>' to return type 'X'
}

即使泛型类型和aliastype匹配,我似乎也无法将对象转换为其协议。

修改

我通过从现有代码中提取逻辑来提出上述代码,以简化问题。这样做我犯了一些错误。这是一个更新的(并且希望不那么令人困惑)代码示例:

protocol Protocol {
    typealias Thing
}
class Class<X>: Protocol {
    typealias Thing = X
}
func test<Y: Protocol where Y.Thing == Int> () -> Y {
    return Class<Y.Thing>()
}

我希望编译器允许test()编译,结果类型为Protocol<Int>

3 个答案:

答案 0 :(得分:6)

今天的Swift你的回归类型是不可能的。具有关联类型(PAT)的协议是抽象的。应用where子句不会改变它。请考虑以下代码:

let x: <WHAT-GOES-HERE?> = test()

x会在哪种类型?没有什么可以写在那里编译。 x.Type会返回什么?你想要的是Protocol where Protocol.Thing == Int,但这不是Swift中的类型。它是一种约束。这是今天可以使用PAT的唯一方法。这就是为什么你不能拥有CollectionType<Int>类型的属性,以及为什么你不能编写test()函数。

该解决方案是一种类型擦除器,可将您的协议转换为具体的结构。例如:

protocol Protocol {
    typealias Thing
    func dosomething() -> Thing?
}
class Class<X>: Protocol {
    typealias Thing = X
    func dosomething() -> Thing? {
        return nil
    }
}

struct AnyProtocol<Thing> {
    var _dosomething: () -> Thing?
    func dosomething() -> Thing? {
        return _dosomething()
    }
}

func test() -> AnyProtocol<Int> {
    return AnyProtocol(_dosomething: Class<Int>().dosomething)
}

Swift的某些未来版本可能会为您自动生成这些类型的橡皮擦,但我不知道任何具体的Swift-evolution建议,所以我们今天必须手工编写它们。

有关构建和使用类型橡皮的更多信息,请参阅A Little Respect for AnySequence

答案 1 :(得分:1)

  

我是疯了还是不应该快速编写代码?

你可能会疯了,但我不会在这里使用你的代码作为测试。但是,代码没有任何意义,所以编译器是正确的抱怨。这对我来说也没有任何意义,所以即使对于一个人来说猜测你的意思也有点困难,但也许你的意思是:

go get

但即使编译,我仍然看不到你得到了什么。首先,我看不出protocol Protocol { typealias Thing } class Class<X>: Protocol { typealias Thing = X } func test<Y:Protocol where Y.Thing == Int> () -> Class<Y> { return Class<Y>() } 中的Y将如何通过实际代码解决。也许(从标题这里判断)问题是你期望泛型类型可以转换为其他泛型类型。他们不是。例如,给定Animal类及其子类Dog,您无法在testMyGeneric<Animal>之间进行交换。

您的评论也没有意义:

  

我要做的是将MyGeneric<Dog>投射到Class<Int>

没有Protocol<Int>这样的类型。只有专门化协议的协议的特定采用者,以便Thing是Int。

答案 2 :(得分:1)

失败的原因很简单:你不尊重你自己定义的细节。

当你使用泛型时,你有一个泛型类型(在这种情况下是X),它不仅仅意味着“任何满足约束的东西”,而是“满足约束的特定东西”。

因此,如果您执行let foo: Class<Int> = test()之类的操作,您的代码仍然可以正常运行。 但是如果你做了像

这样的事情
class AnotherClass<X>: Protocol {
  typealias Thing = X
}
let bar: AnotherClass<Int> = test()

你清楚地看到上述原因是错误的。

这里的主要概念是,当你使用泛型时,你只能使用泛型给你的东西。在这种情况下,请仅使用XX.Thing,即Int。除此之外,除非您在Protocol中添加了某些内容或在函数test

中添加了其他约束

更新

我现在看到更新后的问题。看看Rob Napier的回答,你想要的是Type Eraser。虽然我认为对存在主义类型的快速演化的讨论会带来这个特征吗?