协议只能用作通用约束,因为它具有 Self 或关联类型要求
Protocol can an only be used as a generic constraint because it has Self or associated type requirements
根据对我的 的回答,我有 2 个协议…
protocol Filters: Encodable {
}
protocol QueryParameters: Encodable {
associatedtype T: Filters
var page: Int { get }
var filters: T { get }
}
然后对于类型 Transaction
,我有...
struct TransactionFilters: Filters {
var isWithdrawal: Bool
}
struct TransactionParamters<T: Filters>: QueryParameters {
var page: Int
var filters: T
}
到目前为止一切都很好。
接下来我添加一个协议,Filterable
,我希望任何符合 Filterable
的类型都能够 return 参数,就像这样…
protocol Filterable {
func parameters() -> QueryParameters
}
struct Transactions: Filterable {
func parameters() -> QueryParameters {
let transactionFilters = TransactionFilters(isWithdrawal: true)
return TransactionParamters(page: 1, filters: transactionFilters)
}
}
但我最终……
error: protocol 'QueryParameters' can only be used as a generic constraint because it has Self or associated type requirements
这似乎是一个非常简单的要求,但我花了 2 天时间尝试了我能想到的所有组合以使其正常工作。现在终于认输了
我需要做什么来解决这个问题?
正如我在评论中提到的。您的代码中缺少的是 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))
}
}
通用的内容您已经完成了。现在您可以创建符合您的协议的不同模型对象
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
}
}
像这样创建您的交易对象:
let transactions = Transactions<TransactionParameters<TransactionFilters>>()
print(transactions.parameters().page)
print(transactions.parameters().filters.isWithdrawal)
您可以创建更多类型的查询参数和过滤器
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)
有点晚了,但我认为为您的问题想出一个干净的解决方案仍然是一个很好的练习。
在 Filterable
协议中,您不能将协议类型 QueryParameters
用作 return 类型,因为 QueryParameters
有一个 associatedtype
。此外,这并不是您真正想要的,因为 return 协议类型并不意味着它符合自身(即 QueryParameters
类型不符合 QueryParameters
)。
相反,您需要做的是创建一个符合协议 QueryParameters
的 associatedtype
以用作 return 类型:
protocol Filterable {
associatedtype T : QueryParameters
func parameters() -> T
}
然后你可以使 Transactions
符合 Filterable
使用你在你的问题中使用的相同功能但是 returning opaque type some QueryParameters
而不是协议类型 QueryParameters
。这样你就可以 return 任何符合 QueryParameters
的类型,这是你真正想做的:
struct Transactions: Filterable {
func parameters() -> some QueryParameters {
let transactionFilters = TransactionFilters(isWithdrawal: true)
return TransactionParameters(page: 1, filters: transactionFilters)
}
}
希望对您有所帮助。顺便说一句,我在回答中修复了 TransactionParameters
中的拼写错误 :)
根据对我的
protocol Filters: Encodable {
}
protocol QueryParameters: Encodable {
associatedtype T: Filters
var page: Int { get }
var filters: T { get }
}
然后对于类型 Transaction
,我有...
struct TransactionFilters: Filters {
var isWithdrawal: Bool
}
struct TransactionParamters<T: Filters>: QueryParameters {
var page: Int
var filters: T
}
到目前为止一切都很好。
接下来我添加一个协议,Filterable
,我希望任何符合 Filterable
的类型都能够 return 参数,就像这样…
protocol Filterable {
func parameters() -> QueryParameters
}
struct Transactions: Filterable {
func parameters() -> QueryParameters {
let transactionFilters = TransactionFilters(isWithdrawal: true)
return TransactionParamters(page: 1, filters: transactionFilters)
}
}
但我最终……
error: protocol 'QueryParameters' can only be used as a generic constraint because it has Self or associated type requirements
这似乎是一个非常简单的要求,但我花了 2 天时间尝试了我能想到的所有组合以使其正常工作。现在终于认输了
我需要做什么来解决这个问题?
正如我在评论中提到的。您的代码中缺少的是 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))
}
}
通用的内容您已经完成了。现在您可以创建符合您的协议的不同模型对象
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
}
}
像这样创建您的交易对象:
let transactions = Transactions<TransactionParameters<TransactionFilters>>()
print(transactions.parameters().page)
print(transactions.parameters().filters.isWithdrawal)
您可以创建更多类型的查询参数和过滤器
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)
有点晚了,但我认为为您的问题想出一个干净的解决方案仍然是一个很好的练习。
在 Filterable
协议中,您不能将协议类型 QueryParameters
用作 return 类型,因为 QueryParameters
有一个 associatedtype
。此外,这并不是您真正想要的,因为 return 协议类型并不意味着它符合自身(即 QueryParameters
类型不符合 QueryParameters
)。
相反,您需要做的是创建一个符合协议 QueryParameters
的 associatedtype
以用作 return 类型:
protocol Filterable {
associatedtype T : QueryParameters
func parameters() -> T
}
然后你可以使 Transactions
符合 Filterable
使用你在你的问题中使用的相同功能但是 returning opaque type some QueryParameters
而不是协议类型 QueryParameters
。这样你就可以 return 任何符合 QueryParameters
的类型,这是你真正想做的:
struct Transactions: Filterable {
func parameters() -> some QueryParameters {
let transactionFilters = TransactionFilters(isWithdrawal: true)
return TransactionParameters(page: 1, filters: transactionFilters)
}
}
希望对您有所帮助。顺便说一句,我在回答中修复了 TransactionParameters
中的拼写错误 :)