Swift syntax for struct: variations ?

I wonder if (and guess no) it is possible to have struct in Swift with variation parts? would be something like this:


struct Item {
    var selectionNum: Int
    var selectionName : String
    var itemCase : Int
    switch itemCase {
          case 0 :
               var paramNum: Int
               var paramImage: NSImage
          case 1 : var paramText: String
}

This would allow to factor many common parameters and just have specific parts depending on the case. It improves code readibility and avoid having elements of struct defined and not used.

WIth the risk because that cannott be checked at compile time.

For those who remember, this existed in Pascal RECORD (aka struct) where such a CASE can be defined at the end of a record (record length is computed on the max length of the CASE items):

RECORD Item {

selectionNum: Integer

selectionName : String

CASE itemCase : Integer OF

0 : (paramNum: Integer

paramImage: Image)

1 : (paramText: String)

END;

Accepted Reply

You need to read the Swift guide, because Swift enums aren't the same as C enums, and you can't just make it up as you go along! Look here:


https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html


under the heading Associated Values.


You can do something like this:


struct Item {
  var selectionNum: Int
  var selectionName : String
  var itemCase : ItemCase
  enum ItemCase {
       case
       Image (Int, NSImage),
       Text (String)
  }
}


(Note that 'enum' defines a new type, so it can be nested inside the type Item, or it can be declared outside — that's got nothing to do with how enums work.)


Typically, you use such enums like this:


switch (item.itemCase)

{
case let .Image (number, image): …
case let .Text (text): …
}

Replies

You can use an enum whose cases have associated values.

I do not understand how and what to do !


I tried this:

    struct Item {
        var selectionNum: Int
        var selectionName : String
        enum itemCase: Int {
        case 0 :
             var paramNum: Int
             var paramImage: NSImage
        case 1 : var paramText: String
        }

Not appreciated by compiler.


So I guess enum should be defined outside the struct ; I tried this, doesn't work either


    enum itemCase: Int {
        case zero = 0
        case one = 1
    }
    struct Item {
        var selectionNum: Int
        var selectionName : String
        var iCase: itemCase
        switch iCase {
        case zero :
             var paramNum: Int
             var paramImage: NSImage
        case one : var paramText: String
        }


at line switch iCase {, I get the message :Expected declaration

You need to read the Swift guide, because Swift enums aren't the same as C enums, and you can't just make it up as you go along! Look here:


https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html


under the heading Associated Values.


You can do something like this:


struct Item {
  var selectionNum: Int
  var selectionName : String
  var itemCase : ItemCase
  enum ItemCase {
       case
       Image (Int, NSImage),
       Text (String)
  }
}


(Note that 'enum' defines a new type, so it can be nested inside the type Item, or it can be declared outside — that's got nothing to do with how enums work.)


Typically, you use such enums like this:


switch (item.itemCase)

{
case let .Image (number, image): …
case let .Text (text): …
}

Thanks for help and for advice. I re-read enum and discovered a lot !


here it goes:


    struct Item {
        var selectionNum: Int
        var selectionName : String
        enum ItemCase {
            case Image (a:Int, b:NSImage?)
            case Text (c: String)
        } 
        var anItemCase : ItemCase
        init(num: Int, name : String, aCase : ItemCase) {
            selectionNum = num
            selectionName = name
            anItemCase = aCase
        }
    }
    var example : Item = Item(num: 0, name: "", aCase: .Image(a:1, b:nil))

I have another problem.


In this example, how do I get the a component of image ?


I want to assign it to x ; I try :


let x = example.anItemCase.a, but doesn't work


Do I need to introduce the Image, but how ?

You're falling foul of real type checking. Because 'example' might be any of its cases, you can't just proceed by ignoring the unwanted possibilities. It's similar to optionals, where you have to check every time you use the optional, and the solution is the same: use syntax to get the desired type-checked value, similar to 'if let'.


The simplest solution, for eyes used to reading C-family code, is to use switch:


let x: Int
switch example.anItemCase {
     case let .Image (a, b): x = a // or case .Image (let a, let b) or case .Image (let a, _) etc
     default: // do something appropriate
}


If you want to use an if, there's is syntax (clunky) for that. You basically use the 'case' pattern directly in an 'if':


if case let .Image (a, b) = example.anItemCase {
     x = a
}
else {
     // do something appropriate
}


You can also do something like this:


guard case let .Image (a, _) = example.anItemCase  else {
  // do something appropriate
}
let x = a

I have a problem when updating the associated values of an enum, depending on the type of data.

Here is the case :

I define an enum:


enum CasData {
    case Cas1 (
        tab1: Array1D<Int>
    case Cas2 (
        tab2: Array<Int>

where Array1D is a class defined by :


class Array1D<T> {
    var matrix : [T]
    private var nRows : Int
  
    init(nbRows: Int, withValue: T) {
        nRows = nbRows
        var tab = Array<T>()
        for _ in 0..<nbRows {
            tab.append(withValue)
        }
        matrix = tab
    }
}

So, Array1D<Int> is essentially the same as Array<Int>.


I initialize both tab1 and tab2 with 0.

When I try to modify the assiated value, the behavior is very different :


var data : CasData
switch data {
     case .Cas1(let tab1):
           tab1.matrix[0] = 5
      case .Cas2(var tab2):          // I need var and not let, otherwise I get a compiler error
              tab2[0] = 5
default : break
}

later in code, I access to the data tab1 is modified, tab2 is not : I get "something" printed but not "something else"


var data2 : CasData
switch data2 {
     case .Cas1(let tab1):
           if tab1.matrix[0] == 5 {
               print("something")
          }
      case .Cas2(let tab2):          // I need var and not let, otherwise I get a compiler error
           if tab2[0] == 5 {
               print("something else")
          }
       default : break
}

So questions:

- Why do I need a var in case .Cas2(var tab2) and not in case .Cas1(let tab1), when the 2 types are essentially the same.

- Why is the value updated in the first case, not the second ; is it because enum are passed by value ?

>>So, Array1D<Int> is essentially the same as Array<Int>.


It's not, though, not even close. For a start, Array1D is a reference (class) type. That changes the semantics dramatically. Note that the subscript setter (tab2[0]=) is a mutating function on a value type, so it requires a 'var' declaration. The other one (tab1.matrix[0]=) isn't modifying the reference, so a 'let' declaration for the reference is fine.

And the difference makes it work in the associated value of enum.


I have even a simpler case : if the associated value is defined as Int, I cannot modify it.


I change the type with MyInt, which is a class defined as


class MyInt {
    var value :Int

    init(withValue: Int) {
        value = withValue
    } 
}

then it works immediately !


So from now on, I will define associated values must be defined as a class type, in order to be passed as reference and not value, and be modifiable.


PS: I tried to declare

case .Cas2(inout tab2): , but not accepted by compiler

The correct way to do it is to assign a new enum value (i.e. case with new associated value) to the enum variable. Think of it this way: if you have just a "normal" enum:


enum SomeEnum: Int { case A = 0, B = 1 }


you can't assign new raw value to the cases:


var x = SomeEnum.A
x.A = 1 // no no

So you do this:


enum SomeEnum { case A (Int), B (Int) }
var x = SomeEnum.A (0)
x = .A (1) // OK


The difference is, of course, that in the first example, all of the cases are overlayed onto a single set of raw values, but in the second example, each of the cases has its own set of "raw" values.

Many thanks.


The difficulty is with the complex types I have, the way to write it becomes very complex (headaches in perspective).


So far, it works well by defining new classes for the associated values types. I will return to clean all this a bit later.


Thanks again for help on this "dark side of Swift" (for me at least).

OK, now I think I have understood how to change the content of associated value :

switch x {

case .A(var value):

value = aNewValue // I change the value

x = SomeEnum.A(aNewValue) // I assign to x

default: break

}

Is this correct ?

Well you don't need this line:


value = aNewValue     // I change the value


'value' is just a local variable of the case .A scope, so changing it doesn't achieve anything.

OK, thanks for your help and patience.