Ok, intentaré explicarte lo que estoy tratando de obtener con un ejemplo mínimo viable: me gustaría tener una estructura como esta:

struct MyStruct {
    let aBool: Bool
    let aInt: Int
    let aHashable: Hashable?
}

Pero, por supuesto, esto no se puede hacer porque:

El protocolo 'Hashable' solo se puede usar como una restricción genérica porque tiene requisitos de tipo Self o asociados

Y esto está bien. Puedo obtener lo que quiero de esta manera:

struct MyStruct<T> where T: Hashable {
    let aBool: Bool
    let aInt: Int
    let aHashable: T?
}

Pero quiero que mi estructura tenga dos init de esta manera:

struct MyStruct<T> where T: Hashable {
    let aBool: Bool
    let aInt: Int
    let aHashable: T?

    init(aBool: Bool, aInt: Int) {
        self.init(aBool: aBool, aInt: aInt, aHashable: nil)
    }

    init(aHashable: T?) {
        self.init(aBool: false, aInt: 0, aHashable: aHashable)
    }

    private init(aBool: Bool, aInt: Int, aHashable: T?) {
        self.aBool = aBool
        self.aInt = aInt
        self.aHashable = aHashable
    }
}

Y si trato de iniciar la estructura de esta manera:

let myStruct = MyStruct(aBool: true, aInt: 10)

Recibo un error:

No se pudo inferir el parámetro genérico 'T'

El problema es que incluso si convierto la estructura en una estructura no genérica (con un par de init genéricos):

struct MyStruct {
    let aBool: Bool
    let aInt: Int
    let aHashable: T?

    init(aBool: Bool, aInt: Int) {
        self.init(aBool: aBool, aInt: aInt, aHashable: nil)
    }

    init<T>(aHashable: T?) where T: Hashable {
        self.init(aBool: false, aInt: 0, aHashable: aHashable)
    }

    private init<T>(aBool: Bool, aInt: Int, aHashable: T?) where T: Hashable {
        self.aBool = aBool
        self.aInt = aInt
        self.aHashable = aHashable
    }
}

Todavía recibo un error. Esta vez en la propiedad almacenada let aHashable: T?:

Uso de tipo no declarado 'T'

¿Cuál es la forma correcta de obtener lo que quiero? Gracias.

1
superpuccio 11 dic. 2019 a las 20:24

2 respuestas

La mejor respuesta

El T que desea en este caso es Never, ya que nunca puede tener un valor. Para definir ese tipo de init, debe restringirlo en una extensión como esta:

extension MyStruct where T == Never {
    init(aBool: Bool, aInt: Int) {
        self.init(aBool: aBool, aInt: aInt, aHashable: nil)
    }
}

En mi opinión, Swift también debería permitir esto como:

init(aBool: Bool, aInt: Int) where T == Never {...}

Pero eso no es actualmente legal Swift. Tienes que ponerlo en una extensión. Es solo un problema de sintaxis.

Para completar, aquí está el código completo:

struct MyStruct<T> where T: Hashable {
    let aBool: Bool
    let aInt: Int
    let aHashable: T?

    init(aHashable: T?) {
        self.init(aBool: false, aInt: 0, aHashable: aHashable)
    }

    private init(aBool: Bool, aInt: Int, aHashable: T?) {
        self.aBool = aBool
        self.aInt = aInt
        self.aHashable = aHashable
    }
}

extension MyStruct where T == Never {
    init(aBool: Bool, aInt: Int) {
        self.init(aBool: aBool, aInt: aInt, aHashable: nil)
    }
}

let myStruct = MyStruct(aBool: true, aInt: 10)
2
Rob Napier 11 dic. 2019 a las 19:17

Probar de esta manera

struct MyStruct<T: Hashable> {
    let aBool: Bool
    let aInt: Int
    let aHashable: T?

    init(aBool: Bool, aInt: Int) {
        self.init(aBool: aBool, aInt: aInt, aHashable: nil)
    }

    init(aHashable: T?) {
        self.init(aBool: false, aInt: 0, aHashable: aHashable)
    }

    private init(aBool: Bool, aInt: Int, aHashable: T?) {
        self.aBool = aBool
        self.aInt = aInt
        self.aHashable = aHashable
    }
}

Que

let myStruct = MyStruct<'Your Hashable Type'>(aBool: true, aInt: 10)
0
Yervand Saribekyan 11 dic. 2019 a las 17:59