swift - @propertyWrapper para tipo opcional: error de inicialización de WrappedValue para valor nulo asignado automáticamente

CorePress2024-01-24  9

Me encontré con un error extraño al utilizar la función propertyWrapper. Código de muestra a continuación:

@propertyWrapper
struct Wrapper<Value> {
    private var parameter: Int?

    var wrappedValue: Value

    init(wrappedValue: Value, parameter: Int? = 0) {
        self.wrappedValue = wrappedValue
        self.parameter = parameter
    }
}

class WrapperTest {
    @Wrapper var valueImplicitNil: Double?                                  // OK
    @Wrapper var valueExplicitNil: Double? = nil                            // OK
    //@Wrapper(parameter: 1) var valueWithParamImplicitNil: Double?           // NOK (Missing argument for parameter 'wrappedValue' in call)
    @Wrapper(parameter: 1) var valueWithParamExplicitNil: Double? = nil    // OK
}

Para valueImplicitNil, Swift le asigna automáticamente nil y luego el inicializador de propertyWrapper asigna automáticamente nil al primer parámetro de inicio envueltoValue; ambos comportamientos se describen claramente en la documentación de Swift.

Por alguna razón, no hace lo mismo con la variable valueWithParamImplicitNil. ¿Alguna idea, por qué es así?

EDITAR: Como se recomendó, informé un problema SR-14411 en bugs.swift.org y estoy usando la solución propuesta a continuación.

Supongo que has empujado al compilador más allá de lo que puede manejar. Probablemente sea legal, pero bastante complejo y los envoltorios de propiedades son bastante nuevos. Recomiendo abrir un informe en bugs.swift.org.

- Rob Napier

27/03/2021 a las 22:30



------------------------------------

Aquí el contenedor de propiedades es una estructura, por lo tanto, todas sus propiedades deben inicializarse cuando se crea una instancia. En su ejemplo, no se proporciona ningún valor, por lo que el compilador emite un error.

La estructura contenedora de su propiedad toma un WrapValue no opcional; no se puede inicializar sin un valor. Si no proporciona uno, obtendrá un error de compilación, como puede ver.

YoSi desea un contenedor de propiedad que no requiera un valor contenedor inicial, entonces debe hacer que WrappedValue sea opcional.

1

1

Pero estoy aportando un valor. Siguiendo la documentación de Swift: (1) Si define una variable opcional sin proporcionar un valor predeterminado, la variable se establece automáticamente en nulo:. (2). Cuando incluye argumentos contenedores de propiedades, también puede especificar un valor inicial mediante la asignación. Swift trata la tarea como un argumento WrapValue y usa el initianalizador que acepta los argumentos que incluyas. Funciona correctamente para la primera variable de la clase WrappedTest. Pero las mismas reglas no funcionan para la tercera variable. No hay diferencia entre ellos excepto el segundo parámetro. ¿Extraño?

-jactmp

27/03/2021 a las 17:14



------------------------------------

Como han dicho otros en los comentarios, esto parece un error del compilador. Básicamente, el compilador debería poder completar el primer parámetro para el inicializador (wrappedValue) también en el segundo caso, ya que claramente puede manejar el valor predeterminado para nil pro.propiedades si no se pasan argumentos a la declaración de propiedad.

Entonces, si @Wrapper var valueImplicitNil: ¿Doble? el compilador lo expande a

private var _valueImplicitNil = Wrapper<Double?>(wrappedValue: nil)
var valueImplicitNil: Double? {
    get { return _valueImplicitNil.wrappedValue }
    set { _valueImplicitNil.wrappedValue = newValue }
}

, por lo que debería ser el caso de @Wrapper(parameter: 1) var valueWithParamImplicitNil: Double?, que debería expandirse a:

private var _valueImplicitNil = Wrapper<Double?>(wrappedValue: nil, parameter: 1)
var valueImplicitNil: Double? {
    get { return _valueImplicitNil.wrappedValue }
    set { _valueImplicitNil.wrappedValue = newValue }
}

Una solución alternativa hasta que esto se solucione en el compilador es agregar otro inicializador que tenga solo el segundo argumento:

init<V>(parameter: Int? = 0) where Value == V? {
    self.init(wrappedValue: nil, parameter: parameter)
}

Al menos este es el enfoque que adoptó AppStorage, por lo que parece que los ingenieros de Apple se encontraron con el mismo problema, pero encontraron una manera de solucionar esta limitación.

1

1

Estuve cerca de encontrar una solución similar, pero no tenía idea de cómo verificar si el valor es opcional. Y aquí está. Muy inteligente (al menos para mí). ¡Muchas gracias! POR CIERTO. Informé un problema SR-14411 según lo recomendado por Rob Napier. En la discusión sobre SR-14411 se hace referencia al problema relacionado (inicialización diferida del contenedor de propiedades con parámetro). Interesante. Gracias de nuevo.

-jactmp

31 de marzo de 2021 a las 12:10

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare