Search code examples
swiftcompiler-construction

How swift implement the default Decodable for struct?


struct Person: Decodable {
    let firstName: String
}
var data = """
{"firstName": "Fai"}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let parsed = try decoder.decode(Person.self, from: data)

JSONDecoder will decode the data, which is comfirmed to Decodable protocol.

So I want to know how swift implement this. But I can not get any idea in the source code: https://github.com/apple/swift/blob/main/stdlib/public/core/Codable.swift

The Decodable protocol only need to implement an init(from decoder: Decoder) function. If I am going to do it, I will make an extension for struct:

extension struct: Decodable {
  init(from decoder: Decoder) {...}
}

But when I delete the Decodable on my example, the compiler give errors:

Instance method 'decode(_:from:)' requires that 'Person' conform to 'Decodable'

So this is not the swift way to implement this. How's swift way? And where's the source code?


Solution

  • You can see what the compiler writes for you using -print-ast:

    echo 'struct Person: Decodable {
        let firstName: String
    }' | swiftc -print-ast -
    

    This will output most of the auto-generated code (it should include all of the Codable conformances, but there are a few other kinds of auto-generated code that won't include their implementation):

    internal struct Person : Decodable {
      internal let firstName: String
      private enum CodingKeys : CodingKey {
        case firstName
        @_implements(Equatable, ==(_:_:)) fileprivate static func __derived_enum_equals(_ a: Person.CodingKeys, _ b: Person.CodingKeys) -> Bool {
          private var index_a: Int
          switch a {
          case .firstName:
    
            index_a = 0
          }
          private var index_b: Int
          switch b {
          case .firstName:
    
            index_b = 0
          }
          return index_a == index_b
        }
        fileprivate func hash(into hasher: inout Hasher) {
          private var discriminator: Int
          switch self {
          case .firstName:
    
            discriminator = 0
          }
          hasher.combine(discriminator)
        }
        private init?(stringValue: String) {
          switch stringValue {
          case "firstName":
    
            self = Person.CodingKeys.firstName
            default:
    
            return nil
          }
    
        }
        private init?(intValue: Int) {
          return nil
        }
        fileprivate var hashValue: Int {
          get {
            return _hashValue(for: self)
          }
        }
        fileprivate var intValue: Int? {
          get {
            return nil
          }
        }
        fileprivate var stringValue: String {
          get {
            switch self {
            case .firstName:
    
              return "firstName"
            }
          }
        }
      }
      internal init(firstName: String)
      internal init(from decoder: Decoder) throws {
        @_hasInitialValue private let container: KeyedDecodingContainer<Person.CodingKeys> = try decoder.container(keyedBy: Person.CodingKeys.self)
    
        self.firstName = try container.decode(String.self, forKey: Person.CodingKeys.firstName)
    
      }
    }
    

    For the full implementation details, see DerivedConformanceCodable.cpp. Probably of most interest to your question is deriveBodyDecodable_init.