我正在尝试在协议中声明一个函数,该函数强制符合它的类型返回相同协议的值,但具有特定的关联类型:
protocol Protocol {
typealias ValueType
var value : ValueType? {get}
func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A
}
这个编译。当我尝试创建一个符合它的类时,我得到了错误:
class AClass<T> : Protocol {
var value : T?
func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A {
return AClass<String>()
}
}
错误是'AClass'无法转换为'A'。
我错过了什么吗?这甚至可能吗?
谢谢
答案 0 :(得分:2)
问题在于将协议约束的通用占位符与协议本身混淆。这是一个更简单的示例,类似于您的代码,试图说清楚:
// first, define a protocol and two structs that conform to it
protocol P { }
struct S1: P { }
struct S2: P { }
// now, a function that returns an object in the form
// of a reference to protocol P
func f() -> P {
// S1 conforms to P so that’s fine
return S1()
}
// ok all well and good, this works fine:
let obj = f()
// now, to do something similar to your example code,
// declare a generic function that returns a generic
// placeholder that is _constrained_ by P
// This will NOT compile:
func g<T: P>() -> T { return S1() }
为什么不编译?
泛型函数的工作方式是在编译时,当你调用函数时,编译器决定占位符T
需要什么类型,然后写一个函数所有出现的T
都替换为该类型。
因此,通过以下示例,T
应替换为S1
:
let obj1: S1 = g()
// because T needs to be S1, the generic function g above is
// rewritten by the compiler like this:
func g() -> S1 { return S1() }
这看起来不错。除了,如果我们希望T
成为S2
该怎么办? S2
符合P
,因此T
的值非常合理。但是这怎么可能有效:
// require our result to be of type S2
let obj2: S2 = g()
// so T gets replaced with S2… but now we see the problem.
// you can’t return S1 from a function that has a return type of S2.
// this would result in a compilation error that S2 is not
// convertible to S1
func g() -> S2 { return S1() }
以下是您收到的错误消息的来源。您的占位符A
可以代表符合Protocol
的任何类型,但您尝试返回符合该协议的特定类型(AClass
) 。所以它不会让你这样做。
答案 1 :(得分:0)
看来你是一个有点误解的仿制药。通用函数在这些函数的调用站点实例化,而不是在每个函数本身。因此,您编写的类型约束表示此函数返回一个值,其类型可以是Protocol
的所有子类型中的任何一种。因此,对于A
的所有子类型,函数定义必须在Protocol
上保持静态正确,而不仅仅是AClass<String>
的{{1}},这只是Protocol
的一种类型。
无论如何,我认为没有直接的方法来实现你想要的,至少在当前的Swift中。
答案 2 :(得分:0)
这似乎在操场上有效......它是否适用于你想要做的事情?
protocol StringProtocol
{
typealias ValueType
var value : ValueType? { get }
func getProtocolString<A where A: StringProtocol, A.ValueType == String>() -> A
}
class StringClass : StringProtocol
{
typealias ValueType = String
var value : ValueType?
init() { }
func getProtocolString<A where A: StringProtocol, A.ValueType == String>() -> A
{
return StringClass() as A
}
}
我仍然没有完全遵循您尝试通过此实施实现的要求。