¿Es alguna vez posible crear una referencia a un elemento de una matriz de estructura?

Aug 19 2020

Dado un Arrayconstruct

    import Foundation
    
    struct Card {
        var flag: String = ""
    }
    
    var cards = Array<Card>()
    cards.append(Card())

La siguiente operación NO modificará el elemento de matriz original

    // A copy is created.
    var cardCopy = cards[0]
    
    // Will NOT modify cards[0] 
    cardCopy.flag = "modify0"
    
    print(cards[0].flag)

La siguiente operación modificará el elemento de matriz original

    // We can modify cards[0] by
    cards[0].flag = "modify"

    print(cards[0].flag)

Sin embargo, no es eficiente en el sentido de que necesitamos realizar un acceso de indexación cada vez. Imagina

    cards[0].flag0 = "modify"
    cards[0].flag1 = "modify"
    cards[0].flag2 = "modify"
    cards[0].flag3 = "modify"
    ...

¿Hay alguna manera de que podamos crear una referencia al elemento de la matriz de la estructura? Para que podamos escribir

// How to create a reference to cards[0]?
var cardReference = ...
    cardReference.flag0 = "modify"
    cardReference.flag1 = "modify"
    cardReference.flag2 = "modify"
    cardReference.flag3 = "modify"
    ...

Una de las posibilidades es reemplazar structcon class. Pero me gustaría explorar otras alternativas antes de hacerlo.

Respuestas

7 gcharita Aug 19 2020 at 11:25

Puede lograrlo usando una función para realizar sus cambios y pasar la Cardestructura por referencia como esta:

func update(card: inout Card) {
    card.flag0 = "modify"
    card.flag1 = "modify"
    card.flag2 = "modify"
    card.flag3 = "modify"
}

var cards = Array<Card>()
cards.append(Card())

update(card: &cards[0])

o incluso mejor mediante el uso de una función mutante en el Cardtipo y pasar como cierre sus cambios de esa manera:

struct Card {
    var flag0: String = ""
    var flag1: String = ""
    var flag2: String = ""
    var flag3: String = ""
    
    mutating func update(block: (inout Card) -> Void) {
        block(&self)
    }
}
    
var cards = Array<Card>()
cards.append(Card())

cards[0].update {
    $0.flag0 = "modify" $0.flag1 = "modify"
    $0.flag2 = "modify" $0.flag3 = "modify"
}

Actualización : para que el segundo enfoque sea aún más reutilizable, puede definir un protocolo como este:

protocol Updatable {
    mutating func update(block: (inout Self) -> Void)
}

extension Updatable {
    mutating func update(block: (inout Self) -> Void) {
        block(&self)
    }
}

y hacer que la Cardestructura se ajuste a ella:

struct Card: Updatable {
    var flag0: String = ""
    var flag1: String = ""
    var flag2: String = ""
    var flag3: String = ""
}

Entonces puedes usarlo como arriba.

DávidPásztor Aug 19 2020 at 11:16

Este es el comportamiento esperado, ya Arrayque a structy structs son tipos de valor.

Lo que necesita es el comportamiento del tipo de referencia, por lo que debe convertir su structa class.

Otra solución para modificar varias propiedades de un tipo de valor de una vez es crear una mutatingfunción que haga eso y llamarla en el elemento de la matriz.

struct Card {
    var flag: String = ""
    var flag2 = ""

    mutating func update(flag: String, flag2: String) {
        self.flag = flag
        self.flag2 = flag2
    }
}

var cards = [Card(flag: "a", flag2: "b")]
cards[0].update(flag: "b", flag2: "c")