Home > Net >  How do you call a function in a struct in the init function?
How do you call a function in a struct in the init function?

Time:01-24

I am looking to have a re-useable function in a struct that I can call in my init method. However, when I do this I get the following error.

'self' used before all stored properties are initialized

How can I use calculateValue in my init method without raising this error? I will be using the calculateValue function in other parts of my struct so I don't want it to live in my init function.

struct Card {
    let face: Face
    let suit: Suit
    let value: Int

    init(face: Face, suit: Suit) {
        self.face = face
        self.suit = suit
        self.value = self.calculateValue(face: face)
    }

    private func calculateValue(face: Face) -> Int {
        if face == Face.two { return 2 }
        if face == Face.three { return 3 }
        if face == Face.four { return 4 }
        if face == Face.five { return 5 }
        if face == Face.six { return 6 }
        if face == Face.seven { return 7 }
        if face == Face.eight { return 8 }
        if face == Face.nine { return 9 }
        return 10
    }
}

CodePudding user response:

I can think of two possible solutions.

  1. Make calculateValue a static function. This works since it doesn't depend on any state.
struct Card {
    let face: Face
    let suit: Suit
    let value: Int

    init(face: Face, suit: Suit) {
        self.face = face
        self.suit = suit
        self.value = Card.calculateValue(face: face)
    }

    private static func calculateValue(face: Face) -> Int {
        if face == Face.two { return 2 }
        if face == Face.three { return 3 }
        if face == Face.four { return 4 }
        if face == Face.five { return 5 }
        if face == Face.six { return 6 }
        if face == Face.seven { return 7 }
        if face == Face.eight { return 8 }
        if face == Face.nine { return 9 }
        return 10
    }
}
  1. Move the functionality to return a value to the Face enum. Here's a partial guess at your Face enum with a computed value property:
enum Face {
    case two
    case three
    case jack

    var value: Int {
        switch self {
        case .two:
            return 2
        case .three:
            return 3
        default:
            return 10
        }
    }
}

Then your Card becomes:

struct Card {
    let face: Face
    let suit: Suit
    let value: Int

    init(face: Face, suit: Suit) {
        self.face = face
        self.suit = suit
        self.value = face.value
    }
}

Though now you don't even need the value property or you can make it a computed property.

CodePudding user response:

My suggestion is to declare the enum with Int raw value. Assigning the initial raw value is sufficient, the compiler infers the sequence

enum Face : Int {
    case two = 2, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace
}

and in Card get the value with a computed property which returns the raw value of face if it's less than 10 otherwise 10.

struct Card {
    let face: Face
    let suit: Suit
    
    var value: Int { 
       return face.rawValue < 10 ? face.rawValue : 10
    }
}

The init method is for free.

CodePudding user response:

How about moving this into Face itself, e.g…

enum Face {
    case two, three, four, five, six, seven, eight, nine, ten, jack, queen, king

    var value: Int {
        switch self {
        case .two: return 2
        case .three: return 3
        case .four: return 4
        case .five: return 5
        case .six: return 6
        case .seven: return 7
        case .eight: return 8
        case .nine: return 9
        default: return 10
        }
    }
}

then your init can be…

struct Card {
    let face: Face
    let suit: Suit
    let value: Int
    
    init(face: Face, suit: Suit) {
        self.face = face
        self.suit = suit
        self.value = face.value
    }    
}

CodePudding user response:

Let me make another suggestion: Just make your Face value have an Int raw value:

enum Face: Int {
    case ace   = 1
    case two   = 2
    case three = 3
    case four  = 4
    case five  = 5
    case six   = 6
    case seven = 7
    case eight = 8
    case nine  = 9
    case ten   = 10
    case jack  = 11
    case queen = 12
    case king  = 13
}

At that point, you can just set self.value = face.rawValue. However, you shouldn't do that, because you'd have a split source of truth. The Card structs don't need a copy of the value. They can just access the value of the face, which they already contain:

struct Card {
    let face: Face
    let suit: Suit
    
    init(face: Face, suit: Suit) {
        self.face = face
        self.suit = suit
    }
    
    var value: Int { self.face.rawValue }
}
  • Related