How to make codable struct with optional field

Say we have

public struct EVMBridgeMessage<P: Codable> : Codable {
    public let Cmd: String
    public let Payload: P?
    public init(c : String, p: P?) {
        self.Cmd = c
        self.Payload = p
    }

    public init(c: String) {
        self.Cmd = c
        self.Payload = nil
    }
}

then in usage we have to do

            let msg = try! JSONEncoder().encode(
              EVMBridgeMessage(c: CMD_REPORT_CHAIN_HEAD, p: BridgeCmdSendBackChainHeader())
            )

but I want to do

            let msg = try! JSONEncoder().encode(EVMBridgeMessage(c: CMD_REPORT_CHAIN_HEAD))

but I can't because generic P cannot be inferred, but why it matters anyway, .Payload will be nil.

doing EVMBridgeMessage<Void> doesn't fit the bill either.

thank you

Void is not decodable. Try with Int or String.

EVMBridgeMessage<Int>(c: CMD_REPORT_CHAIN_HEAD)

so I just have to give any type even though it will be set to nil anyway?

1 Like

Overall, this doesn't have to do with Codable — this case, stripped down, is equivalent to

struct S<T> {
    let p: T?
    
    init(_ p: T? = nil) {
        self.p = p
    }
}

let s = S() // => ❌ Generic parameter 'T' could not be inferred

In order to be able to instantiate S, the compiler needs to know how to lay out S in memory, and doing so depends on the type of T; it doesn't matter that the value of p will be nil, because the compiler has to reserve enough space for any value of T?, and that must be known at compile time.

In order to create an S, you need to explicitly give T:

let s = S<Int>() // ✅

In your specific case, because you also have a requirement that T: Codable, you will specifically need to pass in a type which is Codable (even if it goes unused).

3 Likes

Yes. Or you can do it in a way that doesn't require type when using:

public struct EVMBridgeMessage<P: Codable> : Codable {
    public let Cmd: String
    public let Payload: P?
    public init(c : String, p: P) {
        self.Cmd = c
        self.Payload = p
    }
}
extension EVMBridgeMessage where P == Bool {
    public init(c: String) {
        self.Cmd = c
        self.Payload = nil
    }
}
EVMBridgeMessage(c: "hello") // ✅
2 Likes

ah thank you, I like this soln