无法在 Swift 的另一个协议中将协议用作关联类型
Unable to use protocol as associatedtype in another protocol in Swift
我有一个协议 Address
,它继承自另一个协议 Validator
,并且 Address
满足扩展中的 Validator
要求。
还有另一个协议 FromRepresentable
,它有一个 associatedType
(ValueWrapper
) 要求,应该是 Validator
。
现在,如果我尝试将 Address
用作 associatedType
,则它不会编译。它说,
Inferred type 'Address' (by matching requirement 'valueForDetail') is
invalid: does not conform to 'Validator'.
这种用法是否违法?我们不应该使用 Address
代替 Validator
,因为所有 Addresses
都是 Validator
。
下面是我正在尝试的代码。
enum ValidationResult {
case Success
case Failure(String)
}
protocol Validator {
func validate() -> ValidationResult
}
//Address inherits Validator
protocol Address: Validator {
var addressLine1: String {get set}
var city: String {get set}
var country: String {get set}
}
////Fulfill Validator protocol requirements in extension
extension Address {
func validate() -> ValidationResult {
if addressLine1.isEmpty {
return .Failure("Address can not be empty")
}
return .Success
}
}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
// Shipping Address conforming to Address protocol.
// It should also implicitly conform to Validator since
// Address inherits from Validator?
struct ShippingAddress: Address {
var addressLine1 = "CA"
var city = "HYD"
var country = "India"
}
// While compiling, it says:
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform
// to 'Validator'.
// But Address confroms to Validator.
enum AddressFrom: Int, FormRepresentable {
case Address1
case City
case Country
func valueForDetail(valueWrapper: Address) -> String {
switch self {
case .Address1:
return valueWrapper.addressLine1
case .City:
return valueWrapper.city
case .Country:
return valueWrapper.country
}
}
}
更新: 提交了 bug.
您有几个问题:
首先,你实际上并没有声明Address实现了Validator
//Address inherits Validator
protocol Address : Validator {
var addressLine1: String {get set}
var city: String {get set}
var country: String {get set}
}
并且您没有为 ValueWrapper 声明关联类型:
typealias ValueWrapper = ShippingAddress
你似乎真的想要 AddressFrom.valueForDetail 参加 ShippingAddress
:
func valueForDetail(valueWrapper: ShippingAddress) -> String {
switch self {
case .Address1:
return valueWrapper.addressLine1
case .City:
return valueWrapper.city
case .Country:
return valueWrapper.country
}
}
总的来说,它看起来像:
enum ValidationResult {
case Success
case Failure(String)
}
protocol Validator {
func validate() -> ValidationResult
}
//Address inherits Validator
protocol Address : Validator {
var addressLine1: String {get set}
var city: String {get set}
var country: String {get set}
}
////Fulfill Validator protocol requirements in extension
extension Address {
func validate() -> ValidationResult {
if addressLine1.isEmpty {
return .Failure("Address can not be empty")
}
return .Success
}
}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
// Shipping Address conforming to Address protocol.
// It should also implicity conform to Validator since
// Address inherits from Validator?
struct ShippingAddress: Address {
var addressLine1 = "CA"
var city = "HYD"
var country = "India"
}
// While compiling, it says:
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform
// to 'Validator'.
// But Address confroms to Validator.
enum AddressFrom: Int, FormRepresentable {
case Address1
case City
case Country
// define associated type for FormRepresentable
typealias ValueWrapper = ShippingAddress
func valueForDetail(valueWrapper: ShippingAddress) -> String {
switch self {
case .Address1:
return valueWrapper.addressLine1
case .City:
return valueWrapper.city
case .Country:
return valueWrapper.country
}
}
}
的问题在于,一旦将协议的 associatedtype
限制为特定(非 @objc
)协议,就必须使用具体类型来满足要求。
这是因为——因此意味着你不能使用Address
来满足协议对符合Validator
的类型的关联类型要求,因为Address
不是符合Validator
.
的类型
正如我所演示的 ,请考虑反例:
protocol Validator {
init()
}
protocol Address : Validator {}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
}
extension FormRepresentable {
static func foo() {
// if ValueWrapper were allowed to be an Address or Validator,
// what instance should we be constructing here?
// we cannot create an instance of a protocol.
print(ValueWrapper.init())
}
}
// therefore, we cannot say:
enum AddressFrom : FormRepresentable {
typealias ValueWrapper = Address
}
最简单的解决方案是放弃 ValueWrapper
关联类型上的 Validator
协议约束,允许您在方法参数中使用抽象类型。
protocol FormRepresentable {
associatedtype ValueWrapper
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: Address) -> String {
// ...
}
}
如果你需要关联的类型约束,并且每个 AddressFrom
实例只需要一个 Address
的具体实现作为输入——你可以使用泛型来实现你的 AddressFrom
使用要在您的方法中使用的给定具体地址类型进行初始化。
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom<T : Address> : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: T) -> String {
// ...
}
}
// replace ShippingAddress with whatever concrete type you want AddressFrom to use
let addressFrom = AddressFrom<ShippingAddress>.Address1
但是,如果您同时需要关联类型约束 和 ,每个 AddressFrom
实例必须能够处理任何类型的 Address
–您将使用类型擦除来将任意 Address
包装在具体类型中。
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
struct AnyAddress : Address {
private var _base: Address
var addressLine1: String {
get {return _base.addressLine1}
set {_base.addressLine1 = newValue}
}
var country: String {
get {return _base.country}
set {_base.country = newValue}
}
var city: String {
get {return _base.city}
set {_base.city = newValue}
}
init(_ base: Address) {
_base = base
}
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: AnyAddress) -> String {
// ...
}
}
let addressFrom = AddressFrom.Address1
let address = ShippingAddress(addressLine1: "", city: "", country: "")
addressFrom.valueForDetail(AnyAddress(address))
我有一个协议 Address
,它继承自另一个协议 Validator
,并且 Address
满足扩展中的 Validator
要求。
还有另一个协议 FromRepresentable
,它有一个 associatedType
(ValueWrapper
) 要求,应该是 Validator
。
现在,如果我尝试将 Address
用作 associatedType
,则它不会编译。它说,
Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform to 'Validator'.
这种用法是否违法?我们不应该使用 Address
代替 Validator
,因为所有 Addresses
都是 Validator
。
下面是我正在尝试的代码。
enum ValidationResult {
case Success
case Failure(String)
}
protocol Validator {
func validate() -> ValidationResult
}
//Address inherits Validator
protocol Address: Validator {
var addressLine1: String {get set}
var city: String {get set}
var country: String {get set}
}
////Fulfill Validator protocol requirements in extension
extension Address {
func validate() -> ValidationResult {
if addressLine1.isEmpty {
return .Failure("Address can not be empty")
}
return .Success
}
}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
// Shipping Address conforming to Address protocol.
// It should also implicitly conform to Validator since
// Address inherits from Validator?
struct ShippingAddress: Address {
var addressLine1 = "CA"
var city = "HYD"
var country = "India"
}
// While compiling, it says:
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform
// to 'Validator'.
// But Address confroms to Validator.
enum AddressFrom: Int, FormRepresentable {
case Address1
case City
case Country
func valueForDetail(valueWrapper: Address) -> String {
switch self {
case .Address1:
return valueWrapper.addressLine1
case .City:
return valueWrapper.city
case .Country:
return valueWrapper.country
}
}
}
更新: 提交了 bug.
您有几个问题:
首先,你实际上并没有声明Address实现了Validator
//Address inherits Validator
protocol Address : Validator {
var addressLine1: String {get set}
var city: String {get set}
var country: String {get set}
}
并且您没有为 ValueWrapper 声明关联类型:
typealias ValueWrapper = ShippingAddress
你似乎真的想要 AddressFrom.valueForDetail 参加 ShippingAddress
:
func valueForDetail(valueWrapper: ShippingAddress) -> String {
switch self {
case .Address1:
return valueWrapper.addressLine1
case .City:
return valueWrapper.city
case .Country:
return valueWrapper.country
}
}
总的来说,它看起来像:
enum ValidationResult {
case Success
case Failure(String)
}
protocol Validator {
func validate() -> ValidationResult
}
//Address inherits Validator
protocol Address : Validator {
var addressLine1: String {get set}
var city: String {get set}
var country: String {get set}
}
////Fulfill Validator protocol requirements in extension
extension Address {
func validate() -> ValidationResult {
if addressLine1.isEmpty {
return .Failure("Address can not be empty")
}
return .Success
}
}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
// Shipping Address conforming to Address protocol.
// It should also implicity conform to Validator since
// Address inherits from Validator?
struct ShippingAddress: Address {
var addressLine1 = "CA"
var city = "HYD"
var country = "India"
}
// While compiling, it says:
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform
// to 'Validator'.
// But Address confroms to Validator.
enum AddressFrom: Int, FormRepresentable {
case Address1
case City
case Country
// define associated type for FormRepresentable
typealias ValueWrapper = ShippingAddress
func valueForDetail(valueWrapper: ShippingAddress) -> String {
switch self {
case .Address1:
return valueWrapper.addressLine1
case .City:
return valueWrapper.city
case .Country:
return valueWrapper.country
}
}
}
associatedtype
限制为特定(非 @objc
)协议,就必须使用具体类型来满足要求。
这是因为Address
来满足协议对符合Validator
的类型的关联类型要求,因为Address
不是符合Validator
.
正如我所演示的
protocol Validator {
init()
}
protocol Address : Validator {}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
}
extension FormRepresentable {
static func foo() {
// if ValueWrapper were allowed to be an Address or Validator,
// what instance should we be constructing here?
// we cannot create an instance of a protocol.
print(ValueWrapper.init())
}
}
// therefore, we cannot say:
enum AddressFrom : FormRepresentable {
typealias ValueWrapper = Address
}
最简单的解决方案是放弃 ValueWrapper
关联类型上的 Validator
协议约束,允许您在方法参数中使用抽象类型。
protocol FormRepresentable {
associatedtype ValueWrapper
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: Address) -> String {
// ...
}
}
如果你需要关联的类型约束,并且每个 AddressFrom
实例只需要一个 Address
的具体实现作为输入——你可以使用泛型来实现你的 AddressFrom
使用要在您的方法中使用的给定具体地址类型进行初始化。
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom<T : Address> : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: T) -> String {
// ...
}
}
// replace ShippingAddress with whatever concrete type you want AddressFrom to use
let addressFrom = AddressFrom<ShippingAddress>.Address1
但是,如果您同时需要关联类型约束 和 ,每个 AddressFrom
实例必须能够处理任何类型的 Address
–您将使用类型擦除来将任意 Address
包装在具体类型中。
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
struct AnyAddress : Address {
private var _base: Address
var addressLine1: String {
get {return _base.addressLine1}
set {_base.addressLine1 = newValue}
}
var country: String {
get {return _base.country}
set {_base.country = newValue}
}
var city: String {
get {return _base.city}
set {_base.city = newValue}
}
init(_ base: Address) {
_base = base
}
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: AnyAddress) -> String {
// ...
}
}
let addressFrom = AddressFrom.Address1
let address = ShippingAddress(addressLine1: "", city: "", country: "")
addressFrom.valueForDetail(AnyAddress(address))