Cannot assign to 'X' in 'Y' in Swift
Asked Answered
S

3

20

I have a dictionary with Structs in it. I am trying to assign the values of the struct when I loop through the dictionary. Swift is telling me cannot assign to 'isRunning' in 'blockStatus'. I haven't been able to find anything in the docs on this particular immutability of dictionaries or structs.
Straight from the playground:

import Cocoa

struct BlockStatus{
 var isRunning = false
 var timeGapForNextRun = UInt32(0)
 var currentInterval = UInt32(0) 
}

var statuses = ["block1":BlockStatus(),"block2":BlockStatus()]

for (block, blockStatus) in statuses{
 blockStatus.isRunning = true
}

cannot assign to 'isRunning' in 'blockStatus'
blockStatus.isRunning = true

This does work if I change the struct to a class.

I am guessing it has something to do with the fact that structs are copied and classes are always referenced?

EDIT: So even if it is copying it.. Why can't I change it? It would net me the wrong result but you can change members of constants just not the constant themselves. For example you can do this:

class A {
    var b = 5
}

let a = A()
a.b = 6
Solferino answered 20/6, 2014 at 13:30 Comment(0)
K
22

Your guess is true.

By accessing blockStatus, you are creating a copy of it, in this case, it's a constant copy (iterators are always constant).

This is similar to the following:

var numbers = [1, 2, 3]

for i in numbers {
   i = 10  //cannot assign here
}

References:

Control Flow

In the example above, index is a constant whose value is automatically set at the start of each iteration of the loop.

Classes and Structures

A value type is a type that is copied when it is assigned to a variable or constant, or when it is passed to a function. [...] All structures and enumerations are value types in Swift

Methods

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.

However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.

You can opt in to this behavior by placing the mutating keyword before the func keyword for that method:

Kop answered 20/6, 2014 at 13:43 Comment(6)
But even if it is a copy I should be able to change the copy no? It would not give me the right result but... It is a constant but you can change members of constants. See my edit.Solferino
@Solferino Not on a constant struct.Kop
Aha. You are correct. Do you know where it says that?Solferino
@Solferino Added a couple of references.Kop
Good answer. I was going crazy about this. I did some further reading and, @Johnston, I have found where it says that the properties of constant structures cannot be changed. Stored Properties of Constant Structure InstancesDressy
I was running into this problem because I had a protocol Foo. If you have an array of Foo and you iterate through the array, you cannot set the properties of the array members because Foo might be a struct. By making Foo a class protocol I made Swift happy again.Debris
A
6

You could loop through the array with an index

for index in 0..<statuses.count {
    // Use your array - statuses[index]
}

that should work without getting "cannot assign"

Association answered 22/6, 2014 at 10:18 Comment(4)
I can't edit this reply, as it should contain at least 6 characters :], so I will correct you here. 1) Correct operator is "..." not ".."; 2) count should be -1 otherwise you will crash the appHendrika
@Hendrika Edited it for you.Zollie
@Hendrika Better still, use the half-open range operator.Chirrup
agree this is clever ;]Hendrika
M
4

If 'Y' in this case is a protocol, subclass your protocol to class. I had a protocol:

 protocol PlayerMediatorElementProtocol {
      var playerMediator:PlayerMediator { get }
 }

and tried to set playerMediator from within my player mediator:

element.playerMediator = self

Which turned into the error cannot asign 'playerMediator' in 'element'

Changing my protocol to inherit from class fixed this issue:

 protocol PlayerMediatorElementProtocol : class {
      var playerMediator:PlayerMediator { get }
 }

Why should it inherit from class?

The reason it should inherit from class is because the compiler doesn't know what kind your protocol is inherited by. Structs could also inherit this protocol and you can't assign to a property of a constant struct.

Magnetize answered 29/4, 2015 at 15:7 Comment(2)
Had the same issue except the property in my protocol was defined with {get set}. Adding class fixed it, but it seems like you should be able to set without it. Anyone know why class is needed?Prismatic
I've updated my answer with the reason it should inherit from class. It's because the possibility of inheritance by structsMagnetize

© 2022 - 2024 — McMap. All rights reserved.