You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift疑问:继承Encodable的Protocol为何不遵循该协议?

Swift协议继承Encodable却无法自动合成的问题解决

最近有朋友碰到这么个坑:明明定义的协议已经继承了Encodable,但遵循协议的结构体却没法自动合成编码实现,报错说协议不遵循Encodable。咱们一起来看看怎么回事。

问题还原

先看一下他写的代码:

首先定义了两个继承自Encodable的协议:

protocol Filters: Encodable { var page: Int { get } }
protocol Parameters: Encodable { var type: String { get } var filters: Filters { get } }

然后创建了对应的结构体实现:

struct BankAccountFilters: Filters {
    var page: Int
    var isWithdrawal: Bool
}
struct BankAccountParameters: Parameters {
    let type: String = "Bank"
    var filters: Filters
}

let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParameters(filters: baf)

结果编译直接报错:

error: type 'BankAccountParameters' does not conform to protocol 'Encodable'
note: cannot automatically synthesize 'Encodable' because 'Filters' does not conform to 'Encodable'

明明Filters已经继承了Encodable,这报错看起来就很矛盾对吧?

为啥会报错?

其实问题出在Swift自动合成Encodable的规则上:Swift要自动生成编码逻辑,必须知道所有存储属性的具体类型都遵循Encodable。虽然Filters协议本身继承了Encodable,但协议类型本身并不是一个具体的可编码类型——Swift不知道你最终会把哪个遵循Filters的结构体赋值给filters属性,它没法提前确定编码的具体逻辑,自然就没法自动合成了。

解决办法

这里有几种实用的解决方案,你可以根据自己的业务场景选:

方案一:把Parameters改成泛型协议

让Parameters协议关联一个具体的Filters类型,这样Swift就能明确知道filters的具体类型,从而自动合成Encodable:

protocol Filters: Encodable { var page: Int { get } }
protocol Parameters: Encodable {
    associatedtype FilterType: Filters
    var type: String { get }
    var filters: FilterType { get }
}

struct BankAccountFilters: Filters {
    var page: Int
    var isWithdrawal: Bool
}
struct BankAccountParameters: Parameters {
    let type: String = "Bank"
    var filters: BankAccountFilters // 这里用具体的结构体类型
}

// 现在可以正常编码了
let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParameters(filters: baf)
try JSONEncoder().encode(bap)

方案二:手动实现Encodable方法

如果不想用泛型,也可以直接给BankAccountParameters手动写encode(to:)方法,明确告诉Swift怎么编码filters属性:

protocol Filters: Encodable { var page: Int { get } }
protocol Parameters: Encodable { var type: String { get } var filters: Filters { get } }

struct BankAccountFilters: Filters {
    var page: Int
    var isWithdrawal: Bool
}
struct BankAccountParameters: Parameters {
    let type: String = "Bank"
    var filters: Filters
    
    // 手动实现编码逻辑
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(type, forKey: .type)
        // 直接编码filters,因为它本身遵循Encodable
        try container.encode(filters, forKey: .filters)
    }
    
    // 定义编码的Key
    enum CodingKeys: String, CodingKey {
        case type, filters
    }
}

let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParameters(filters: baf)
try JSONEncoder().encode(bap)

方案三:用类型擦除包装协议类型

如果你的业务需要支持多种Filters类型,还可以用类型擦除把协议类型包装成一个具体的结构体AnyFilters,这样就能满足自动合成的要求了:

protocol Filters: Encodable { var page: Int { get } }

// 类型擦除的结构体,把Filters协议包装成具体类型
struct AnyFilters: Filters, Encodable {
    let page: Int
    private let encodeClosure: (Encoder) throws -> Void
    
    init<F: Filters>(_ filters: F) {
        self.page = filters.page
        self.encodeClosure = { encoder in
            try filters.encode(to: encoder)
        }
    }
    
    func encode(to encoder: Encoder) throws {
        try encodeClosure(encoder)
    }
}

protocol Parameters: Encodable { var type: String { get } var filters: AnyFilters { get } }

struct BankAccountFilters: Filters {
    var page: Int
    var isWithdrawal: Bool
}
struct BankAccountParameters: Parameters {
    let type: String = "Bank"
    var filters: AnyFilters
}

let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParameters(filters: AnyFilters(baf))
try JSONEncoder().encode(bap)

内容的提问来源于stack exchange,提问作者Ashley Mills

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:55:47