Home > Mobile >  Fixing "ambiguous use of" compiler error in swift
Fixing "ambiguous use of" compiler error in swift

Time:03-25

As part of my SingleValueDecodingContainer, I tried to make all FixedWidthInteger types use the same decoding method, however this leads to an "ambiguous use of" error in my Bool decoder that I don't know how to fix (Bools are encoded as Int8 in this format). Here is the affected code :

class SingleValueContainer : SingleValueDecodingContainer {
        var storage : Data = Data()
        
        var codingPath: [CodingKey]
        
        init(from data : Data, codingPath : [CodingKey] = []) {
            self.codingPath = codingPath
            self.storage = data
        }
        
        func decode(_ type: Bool.Type) throws -> Bool {
            guard let value = try? decode(Int8.self) else { // Ambiguous use of 'decode'
                let context = DecodingError.Context(
                    codingPath: self.codingPath,
                    debugDescription: "Could not decode UInt8 representation of boolean"
                )
                throw DecodingError.typeMismatch(Bool.self, context)
            }
            
            if value == 1 {
                return true
            } else if value == 0 {
                return false
            } else {
                let context = DecodingError.Context(
                    codingPath: self.codingPath,
                    debugDescription: "Incorrect value for UInt8 representation of boolean : \(value)"
                )
                throw DecodingError.typeMismatch(type, context)
            }
        }

        
        func decode<T: FixedWidthInteger>(_ type : T.Type) throws -> T {
            guard storage.count == type.bitWidth / 8 else {
                let context = DecodingError.Context(
                    codingPath: self.codingPath,
                    debugDescription: "Invalid number of bytes, expected \(type.bitWidth) instead of \(storage.count)"
                )
                throw DecodingError.typeMismatch(type, context)
            }
            
            return T(data : storage)
        }
    }

Do I have to make separate methods for all FixedWidthInteger types which call the generic one, or is there a way to fix with the error without doing that ?


Here is also the extension I made for FixedWidthInteger to initialise them from Data and [UInt8], as I think it is necessary to understand the code above :

extension FixedWidthInteger {
    init(bytes : [UInt8]) {
        self.init(
            bigEndian : bytes.withUnsafeBufferPointer {
                 ($0.baseAddress!.withMemoryRebound(to: Self.self, capacity: 1) { $0 })
            }.pointee
        )
    }
    
    init(data : Data) {
        self.init(bytes : Array(data))
    }
}

CodePudding user response:

Most FixedWidthInteger conforming types already implement Decodable. It's possible that the default implementation isn't what you want, but you can't change it.

If the default implementation works for you, you can get rid of your decode<T: FixedWidthInteger>.

If you want a different way of reading these, you need a different name for decode<T: FixedWidthInteger> so there's no ambiguity about which you mean. For example, you can rename it read:

func read<T: FixedWidthInteger>(_ type : T.Type) throws -> T { ... }

And then change your decode line to:

guard let value = try? read(Int8.self) else {

CodePudding user response:

Someone posted this as a comment, then deleted it, but I think it is the right answer : The reason it doesn't work is that I also had another generic decode override, that is required by the protocol :

func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
    <#code#> // I haven't implemented this one yet, pretend there's code here
}

Which means that to resolve the ambiguous call, the generic for FixedWidthInteger must instead be for FixedWidthInteger & Decodable :

func decode<T: FixedWidthInteger & Decodable>(_ type : T.Type) throws -> T {
    guard storage.count == type.bitWidth / 8 else {
        let context = DecodingError.Context(
            codingPath: self.codingPath,
            debugDescription: "Invalid number of bytes, expected \(type.bitWidth) instead of \(storage.count)"
        )
        throw DecodingError.typeMismatch(type, context)
    }
            
    return T(data : storage)
}

And now everything works !

CodePudding user response:

Because this functions take bool type not UInt8 type.

guard let value = try? decode(Bool.self) else{  }

Well, why don't you use "Bool.self" instead of "UInt8.self" and then compare value in true and false.

You can create another short function that tells you either its 1 or 0 by calling it like this

if value.intValue == 1{
// do your stuff
}
//Here is conversion extension for bool to Int
extension Bool {
var intValue: Int {
    return self ? 1 : 0
}
}
  • Related