14 Replies
      Latest reply on Mar 30, 2016 3:27 PM by Claude31
      Claude31 Level 8 Level 8 (6,345 points)

        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;

        • Re: Swift syntax for struct: variations ?
          QuinceyMorris Level 8 Level 8 (6,010 points)

          You can use an enum whose cases have associated values.

            • Re: Swift syntax for struct: variations ?
              Claude31 Level 8 Level 8 (6,345 points)

              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

                • Re: Swift syntax for struct: variations ?
                  QuinceyMorris Level 8 Level 8 (6,010 points)

                  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): …
                  }
                  
                    • Re: Swift syntax for struct: variations ?
                      Claude31 Level 8 Level 8 (6,345 points)

                      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))
                      
                        • Re: Swift syntax for struct: variations ?
                          Claude31 Level 8 Level 8 (6,345 points)

                          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 ?

                            • Re: Swift syntax for struct: variations ?
                              QuinceyMorris Level 8 Level 8 (6,010 points)

                              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
                              
                                • Re: Swift syntax for struct: variations ?
                                  Claude31 Level 8 Level 8 (6,345 points)

                                  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 ?

                                    • Re: Swift syntax for struct: variations ?
                                      QuinceyMorris Level 8 Level 8 (6,010 points)

                                      >>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.

                                        • Re: Swift syntax for struct: variations ?
                                          Claude31 Level 8 Level 8 (6,345 points)

                                          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

                                            • Re: Swift syntax for struct: variations ?
                                              QuinceyMorris Level 8 Level 8 (6,010 points)

                                              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.