协议只能用作通用约束,因为它具有 Self 或关联类型要求

Ash*_*lls 4 generics swift swift-protocols

根据我上一个问题的答案,我有 2 个协议\xe2\x80\xa6

\n\n
protocol Filters: Encodable { \n}\n\nprotocol QueryParameters: Encodable {\n    associatedtype T: Filters\n    var page: Int { get }\n    var filters: T { get }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后对于类型Transaction,我有\xe2\x80\xa6

\n\n
struct TransactionFilters: Filters {\n    var isWithdrawal: Bool\n}\n\nstruct TransactionParamters<T: Filters>: QueryParameters {\n    var page: Int\n    var filters: T\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

到目前为止一切都很好。

\n\n

接下来我添加一个协议 ,Filterable并且我希望任何符合该协议的类型都Filterable能够返回参数,如下所示\xe2\x80\xa6

\n\n
protocol Filterable {\n    func parameters() -> QueryParameters\n}\n\nstruct Transactions: Filterable {\n    func parameters() -> QueryParameters {\n        let transactionFilters = TransactionFilters(isWithdrawal: true)\n        return TransactionParamters(page: 1, filters: transactionFilters)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但我最终得到\xe2\x80\xa6

\n\n
\n

错误:协议“QueryParameters”只能用作通用约束,因为它具有 Self 或关联的类型要求

\n
\n\n

这似乎是一个非常简单的要求,但我花了两天时间尝试了我能想到的所有组合以使其发挥作用。现在我终于认输了。

\n\n

我需要做什么来解决这个问题?

\n

Au *_*Ris 6

正如我在评论中提到的。您的代码中缺少的是associatedtype永远不会真正成为一种类型。在您的代码中的任何地方都没有structs将类型分配给associatedtype. 如果您想要通用filterable功能,您可以按照以下方式做一些事情:

// Your generic Filters with required properties
protocol Filters: Encodable {
    var isWithdrawal: Bool { get }
    init(isWithdrawal: Bool)
}

// Your generic query parameters
protocol QueryParameters: Encodable {
    associatedtype F: Filters
    var page: Int { get }
    var filters: F { get }

    init(page: Int, filters: Filters)
}

// Filterable protocol will eventually accept any types conforming to Filters and QueryParameters
protocol Filterable {
    associatedtype F: Filters
    associatedtype P: QueryParameters

    func parameters() -> P
}

// This is your generic Transactions struct
// With this you will be able to pass other types that meet the constraints
struct Transactions<ParameterType: QueryParameters>: Filterable {
    typealias P = ParameterType
    typealias F = ParameterType.F

    func parameters() -> ParameterType {
        return P(page: 1, filters: F(isWithdrawal: true))
    }
} 
Run Code Online (Sandbox Code Playgroud)

你已经完成了通用的东西。现在您可以创建符合您的协议的不同模型对象

struct TransactionFilters: Filters {
    private(set) var isWithdrawal: Bool // Conforming to filters
}

struct TransactionParameters<FilterType: Filters>: QueryParameters {
    // Telling what type is the object that conforms to Filters
    typealias F = FilterType
    var page: Int
    var filters: FilterType

    init(page: Int, filters: Filters) {
        self.page = page
        self.filters = filters as! F
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样创建您的交易对象:

let transactions = Transactions<TransactionParameters<TransactionFilters>>()
print(transactions.parameters().page)
print(transactions.parameters().filters.isWithdrawal)
Run Code Online (Sandbox Code Playgroud)

您可以创建更多类型的查询参数和过滤器

struct SomeOtherParameters<FilterType: Filters>: QueryParameters {
    // You can do custom stuff in your SomeOtherParameters struct
    // e.g. add an offset to page
    typealias F = FilterType
    var page: Int
    var filters: FilterType

    init(page: Int, filters: Filters) {
        self.page = page + 100
        self.filters = filters as! F
    }
}

// Your special filter that always returns false
struct SomeOtherFilters: Filters {
    init(isWithdrawal: Bool) {}

    var isWithdrawal: Bool {
        return false
    }
}

let transactionsWithDifferentFilters = Transactions<SomeOtherParameters<SomeOtherFilters>>()

// You can combine any types that conform to you declared protocols
let evenMoreTransactions = Transactions<SomeOtherParameters<TransactionFilters>>()
print(evenMoreTransactions.parameters().page)
Run Code Online (Sandbox Code Playgroud)