O atributo de mutabilidade é marcado em um armazenamento (constante ou variável), não em um tipo. Você pode pensar que struct tem dois modos: mutável e imutável . Se você atribuir um valor de estrutura a um armazenamento imutável (nós o chamamos let
ou constante em Swift), o valor se torna o modo imutável e você não pode alterar nenhum estado no valor. (incluindo chamar qualquer método mutante)
Se o valor é atribuído a um armazenamento mutável (chamamos-lhe var
ou variável em Swift), você está livre para modificar o estado deles, e chamando de método de mutação é permitido.
Além disso, as classes não têm esse modo imutável / mutável. IMO, isso ocorre porque as classes são geralmente usadas para representar entidades referenciadas . E a entidade referenciável geralmente é mutável porque é muito difícil fazer e gerenciar gráficos de referência de entidades de maneira imutável com desempenho adequado. Eles podem adicionar esse recurso mais tarde, mas não agora, pelo menos.
Para programadores Objective-C, os conceitos mutáveis / imutáveis são muito familiares. Em Objective-C, tínhamos duas classes separadas para cada conceito, mas em Swift, você pode fazer isso com uma estrutura. Meio trabalho.
Para programadores C / C ++, este também é um conceito muito familiar. Isso é exatamente o que as const
palavras-chave fazem em C / C ++.
Além disso, o valor imutável pode ser muito bem otimizado. Em teoria, o compilador Swift (ou LLVM) pode realizar a elisão de cópia em valores passados let
, assim como em C ++. Se você usar a estrutura imutável com sabedoria, ela terá melhor desempenho do que as classes contadas novamente.
Atualizar
Como @Joseph afirmou que isso não fornece o motivo , estou adicionando um pouco mais.
As estruturas têm dois tipos de métodos. métodos simples e mutantes . O método simples implica imutável (ou sem mutação) . Essa separação existe apenas para oferecer suporte à semântica imutável . Um objeto em modo imutável não deve mudar seu estado.
Então, os métodos imutáveis devem garantir essa imutabilidade semântica . O que significa que não deve alterar nenhum valor interno. Portanto, o compilador não permite qualquer mudança de estado em um método imutável. Em contraste, os métodos mutantes são livres para modificar estados.
E então, você pode se perguntar por que imutável é o padrão? Isso porque é muito difícil prever o estado futuro de valores mutantes e isso geralmente se torna a principal fonte de dores de cabeça e insetos. Muitas pessoas concordaram que a solução é evitar coisas mutáveis, e então imutável por padrão estava no topo da lista de desejos por décadas nas linguagens da família C / C ++ e suas derivações.
Veja o estilo puramente funcional para mais detalhes. De qualquer forma, ainda precisamos de coisas mutáveis porque coisas imutáveis têm alguns pontos fracos, e discutir sobre elas parece estar fora de assunto.
Eu espero que isso ajude.
const
a ao método se quiser que ele tenha um 'const this' e seja permitido em instâncias constantes (métodos normais não podem ser usados se a instância for const). O Swift faz o mesmo com o padrão oposto: um método tem um 'const this' por padrão e você o marca caso contrário, se tiver que ser proibido para instâncias constantes.Uma estrutura é uma agregação de campos; se uma instância de estrutura particular for mutável, seus campos serão mutáveis; se uma instância for imutável, seus campos serão imutáveis. Um tipo de estrutura deve, portanto, ser preparado para a possibilidade de que os campos de qualquer instância particular possam ser mutáveis ou imutáveis.
Para que um método de estrutura modifique os campos da estrutura subjacente, esses campos devem ser mutáveis. Se um método que modifica campos da estrutura subjacente é invocado em uma estrutura imutável, ele tentaria modificar campos imutáveis. Uma vez que nada de bom poderia resultar disso, tal invocação precisa ser proibida.
Para conseguir isso, o Swift divide os métodos de estrutura em duas categorias: aqueles que modificam a estrutura subjacente e, portanto, só podem ser invocados em instâncias de estrutura mutável, e aqueles que não modificam a estrutura subjacente e, portanto, devem ser invocáveis em instâncias mutáveis e imutáveis . O último uso é provavelmente o mais frequente e, portanto, o padrão.
Em comparação, o .NET atualmente (ainda!) Não oferece meios de distinguir métodos de estrutura que modificam a estrutura daqueles que não a modificam. Em vez disso, invocar um método de estrutura em uma instância de estrutura imutável fará com que o compilador faça uma cópia mutável da instância de estrutura, deixe o método fazer o que quiser com ela e descartar a cópia quando o método for concluído. Isso tem o efeito de forçar o compilador a perder tempo copiando a estrutura, quer o método a modifique ou não, mesmo que adicionar a operação de cópia quase nunca transforme o que seria código semanticamente incorreto em código semanticamente correto; simplesmente fará com que o código que é semanticamente errado de uma maneira (modificando um valor "imutável") esteja errado de uma maneira diferente (permitindo que o código pense que está modificando uma estrutura, mas descartando as mudanças tentadas). Permitir que os métodos de estrutura indiquem se eles modificarão a estrutura subjacente pode eliminar a necessidade de uma operação de cópia inútil e também garante que a tentativa de uso incorreto seja sinalizada.
fonte
Cuidado: termos do leigo à frente.
Esta explicação não é rigorosamente correta no nível de código mais básico. No entanto, foi revisado por um cara que realmente trabalha no Swift e ele disse que é bom o suficiente como uma explicação básica.
Portanto, quero tentar responder de forma simples e direta à pergunta "por que".
Para ser mais preciso: por que temos que marcar as funções de estrutura como
mutating
quando podemos alterar os parâmetros de estrutura sem nenhuma palavra-chave de modificação?Portanto, o quadro geral tem muito a ver com a filosofia que mantém o Swift rápido.
Você pode pensar nisso como o problema de gerenciar endereços físicos reais. Quando você muda seu endereço, se houver muitas pessoas que têm o seu atual, você deve avisar a todas que você se mudou. Mas se ninguém tiver seu endereço atual, você pode simplesmente ir para onde quiser e ninguém precisa saber.
Nessa situação, o Swift é como um correio. Se muitas pessoas com muitos contatos se movimentam muito, a sobrecarga é muito alta. É necessário pagar uma grande equipe para lidar com todas essas notificações, e o processo exige muito tempo e esforço. É por isso que o estado ideal de Swift é que todos em sua cidade tenham o mínimo de contatos possível. Assim, ele não precisa de uma grande equipe para lidar com as alterações de endereço e pode fazer todo o resto de maneira mais rápida e melhor.
É também por isso que o pessoal da Swift está falando a respeito de tipos de valor versus tipos de referência. Por natureza, os tipos de referência acumulam "contatos" em todos os lugares, e os tipos de valor geralmente não precisam de mais do que alguns. Os tipos de valor são "Swift" -er.
Então, de volta a pequena imagem:
structs
. Structs são muito importantes no Swift porque podem fazer a maioria das coisas que os objetos podem fazer, mas são tipos de valor.Vamos continuar a analogia do endereço físico imaginando um
misterStruct
que vive emsomeObjectVille
. A analogia fica um pouco complicada aqui, mas acho que ainda é útil.Portanto, para modelar a alteração de uma variável em a
struct
, digamos quemisterStruct
tenha cabelo verde e receba uma ordem para mudar para cabelo azul. A analogia fica complicada, como eu disse, mas mais ou menos o que acontece é que em vez de mudarmisterStruct
o cabelo, a pessoa velha se muda e uma nova pessoa com cabelo azul entra, e essa nova pessoa começa a se chamarmisterStruct
. Ninguém precisa receber uma notificação de mudança de endereço, mas se alguém olhar para esse endereço, verá um cara de cabelo azul.Agora vamos modelar o que acontece quando você chama uma função em a
struct
. Nesse caso, é comomisterStruct
obter um pedido comochangeYourHairBlue()
. Assim, o correio entrega a instrução demisterStruct
"mude seu cabelo para azul e diga-me quando terminar".Se ele está seguindo a mesma rotina de antes, se está fazendo o que fazia quando a variável foi alterada diretamente, o que
misterStruct
vai fazer é sair de sua própria casa e chamar uma nova pessoa de cabelo azul. Mas esse é o problema.A ordem era "vá mudar seu cabelo para azul e me diga quando terminar", mas foi o cara verde que recebeu o pedido. Depois que o cara azul se muda, uma notificação de "trabalho concluído" ainda precisa ser enviada de volta. Mas o cara azul não sabe nada sobre isso.
[Para realmente entender essa analogia, algo horrível, o que tecnicamente aconteceu com o cara de cabelo verde foi que, depois que ele se mudou, ele imediatamente cometeu suicídio. Portanto, ele também não pode notificar ninguém de que a tarefa está concluída ! ]
Para evitar este problema, em casos como este única , Swift tem que ir diretamente para a casa naquele endereço e realmente mudar o cabelo do habitante atual . Esse é um processo completamente diferente do que apenas enviar um novo cara.
E é por isso que Swift deseja que usemos a
mutating
palavra - chave!O resultado final parece o mesmo para qualquer coisa que se refira à estrutura: o habitante da casa agora tem cabelo azul. Mas os processos para alcançá-lo são completamente diferentes. Parece que está fazendo a mesma coisa, mas está fazendo algo muito diferente. Está fazendo uma coisa que as estruturas do Swift em geral nunca fazem.
Portanto, para dar ao pobre compilador uma pequena ajuda, e não fazer com que ele descubra se uma função sofre mutação
struct
ou não, por conta própria, para cada função de estrutura já existente, somos solicitados a ter pena e usar amutating
palavra - chave.Em essência, para ajudar a Swift a permanecer ágil, todos devemos fazer nossa parte. :)
EDITAR:
Ei cara / dudette que me rebaixou, eu apenas reescrevi completamente minha resposta. Se ficar melhor com você, você removerá o voto negativo?
fonte
As estruturas Swift podem ser instanciadas como constantes (via
let
) ou variáveis (viavar
)Considere a
Array
estrutura do Swift (sim, é uma estrutura).Por que o anexo não funcionou com os nomes dos planetas? Porque append é marcado com a
mutating
palavra - chave. E desde queplanetNames
foi declarado usandolet
, todos os métodos marcados estão fora dos limites.Em seu exemplo, o compilador pode dizer que você está modificando a estrutura atribuindo a uma ou mais de suas propriedades fora de um
init
. Se você alterar seu código um pouco, verá issox
ey
nem sempre está acessível fora da estrutura. Observe olet
na primeira linha.fonte
Considere uma analogia com C ++. Um método struct em Swift sendo
mutating
/ não-mutating
é análogo a um método em C ++ sendo não-const
/const
. Um método marcadoconst
em C ++ de maneira semelhante não pode alterar a estrutura.Em C ++, você também pode "alterar uma var de fora da estrutura" - mas apenas se tiver uma
const
variável não estrutural. Se você tem umaconst
variável de estrutura, não pode atribuir a uma var e também não pode chamar um não-const
método. Da mesma forma, no Swift, você pode alterar uma propriedade de uma estrutura somente se a variável da estrutura não for uma constante. Se você tiver uma constante de estrutura, não poderá atribuir a uma propriedade e também não poderá chamar ummutating
método.fonte
Eu me perguntei a mesma coisa quando comecei a aprender Swift, e cada uma dessas respostas, embora adicionando alguns insights, talvez, são meio prolixas e confusas por direito próprio. Acho que a resposta à sua pergunta é bem simples ...
O método mutante definido dentro de sua estrutura precisa de permissão para modificar cada uma das instâncias de si mesmo que serão criadas no futuro. E se uma dessas instâncias for atribuída a uma constante imutável com
let
? Uh oh. Para protegê-lo de você mesmo (e para permitir que o editor e o compilador saibam o que você está tentando fazer), você é forçado a ser explícito quando deseja dar a um método de instância esse tipo de poder.Por outro lado, a configuração de uma propriedade de fora de sua estrutura está operando em uma instância conhecida dessa estrutura. Se for atribuído a uma constante, o Xcode avisará você sobre isso no momento em que você digitar a chamada do método.
Essa é uma das coisas que adoro no Swift à medida que começo a usá-lo mais - ser alertado sobre erros enquanto os digito. Com certeza é melhor do que solucionar bugs obscuros de JavaScript!
fonte
SWIFT: Uso de função mutante em Structs
Os programadores Swift desenvolveram Structs de tal forma que suas propriedades não podem ser modificadas nos métodos Struct. Por exemplo, verifique o código fornecido abaixo
Ao executar o código acima, obtemos um erro porque estamos tentando atribuir um novo valor à população de propriedades da Struct City. Por padrão, as propriedades de Structs não podem ser alteradas dentro de seus próprios métodos. É assim que os desenvolvedores da Apple o construíram, de modo que os Structs terão uma natureza estática por padrão.
Como resolvemos isso? Qual é a alternativa?
Palavra-chave mutante:
Declarar função como mutante dentro de Struct nos permite alterar propriedades em Structs. Linha nº: 5, do código acima muda para assim,
Agora podemos atribuir o valor de newpopulation à população de propriedade dentro do escopo do método.
Nota :
Se usarmos let em vez de var para objetos Struct, então não podemos alterar o valor de nenhuma propriedade. Esta é a razão pela qual obtemos um erro quando tentamos invocar a função mutante usando a instância let. Portanto, é melhor usar var sempre que estiver alterando o valor de uma propriedade.
Adoraria ouvir seus comentários e pensamentos ...
fonte