简体   繁体   English

协议上的关联类型和泛型

[英]Associated types and generics on protocols

I'm trying to declare a function in a protocol that forces types that conform to it to return a value of that same protocol but with a specific associated type: 我试图在协议中声明一个函数,该函数强制符合它的类型返回相同协议的值,但具有特定的关联类型:

protocol Protocol {
    typealias ValueType

    var value : ValueType? {get}

    func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A
}

This compiles. 这编译。 It's when I try to create a class that conforms to it that I get the errors: 当我尝试创建一个符合它的类时,我得到了错误:

class AClass<T> : Protocol {
    var value : T?       

    func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A {
        return AClass<String>()
    }
}

The error is 'AClass' is not convertible to 'A' . 错误是'AClass'不能转换为'A'

Am I missing something? 我错过了什么吗? Is this even possible? 这甚至可能吗?

Thank you 谢谢

The problem lies with confusing a generic placeholder constrained by a protocol, with the protocol itself. 问题在于将协议约束的通用占位符与协议本身混淆。 Here's a simpler example, similar to your code, to try and make it clear: 这是一个更简单的示例,类似于您的代码,试图说清楚:

// 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() }

Why does this not compile? 为什么这不编译?

The way generic functions work is that at compile time , when you call the function, the compiler decides what type the placeholder T needs to be, and then writes you a function with all occurrences of T replaced with that type. 泛型函数的工作方式是在编译时 ,当您调用函数时,编译器决定占位符T需要的类型,然后为您写入一个函数,其中所有出现的T替换为该类型。

So with the example below, T should be replaced by S1 : 因此,通过下面的示例, 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() }

This looks OK. 这看起来不错。 Except, what if we wanted T to be S2 ? 除了,如果我们想让T成为S2怎么办? S2 conforms to P so is a perfectly legitimate value for T . S2符合P因此对于T来说是完全合法的值。 But how could this work: 但这怎么可行呢:

// 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() }

Here is the origin of the error message you are getting. 这是您获得的错误消息的来源。 Your placeholder A can stand in for any type that conforms to Protocol , but you are trying to return a specific type ( AClass ) that conforms to that protocol. 您的占位符A可以代表符合Protocol任何类型,但您尝试返回符合该协议的特定类型( AClass )。 So it won't let you do it. 所以它不会让你这样做。

It seems that you are a little misunderstanding generics. 看来你是一个有点误解的仿制药。 Generic functions are instantiated at call sites of these, not at each function itself body. 通用函数在这些函数的调用站点实例化,而不是在每个函数本身。 So, the type constraints you've written are saying that this function returns a value, a type of which may be any of all the subtypes of the Protocol . 因此,您编写的类型约束表示此函数返回一个值,其类型可以是Protocol的所有子类型中的任何一种。 Consequently, the function definition must be statically correct about A for all the subtypes of Protocol , not only for AClass<String> , which is only an one type of Protocol . 因此,对于Protocol所有子类型,函数定义必须在静态上对于A是正确的,不仅对于AClass<String> ,它只是一种类型的Protocol

In any case, I think there is no direct way to achieve what you want, at least in current Swift. 在任何情况下,我认为没有直接的方法来实现你想要的,至少在当前的Swift中。

This seemed to work in the playground... does it work for what you are trying to do? 这似乎在操场上有效......它是否适用于你想要做的事情?

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
    }
}

I'm still not exactly following what requirements you are trying to fulfill with this implementation. 我仍然没有完全遵循您尝试通过此实现实现的要求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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